微信公众平台支付开发详解

2025-05-29 0 71

公众号支付就是在微信里面的H5页面唤起微信支付,不用扫码即可付款的功能。做这个功能首先要明确的就是,只有和商户号mch_id匹配的appid才能成功支付。商户号在注册成功的时候就会将相关信息发送到邮箱里面。而唤起支付的一个关键是靠openid拿到统一下单。而openid是和appid一一对应的。也就是说如果你登录使用的appid不是公众号的appid,得到的openid就无法唤起公众号内的支付(会出现appid和商户号不匹配的错误)。曾经就在这个地方绕了个弯,因为微信的开放平台可以创建网站应用,也有一个appid和appsecreat,也可以在微信里面一键登录。

业务流程

下面是微信的官方流程,看似有点复杂,重点就是要拿到统一下单接口返回的json串,其他按照官方demo基本就能正确,下面说一下几个细节。

微信公众平台支付开发详解

创建订单

在调用微信公众号支付之前,首先我们自己要把订单创建好。比如一个充值的订单。主要是先确定下金额再进行下一步。

?

1

2

3

4

5

6

7
public JsonResult CreateRecharegOrder(decimal money)

{

if (money < (decimal)0.01) return Json(new PaymentResult("充值金额非法!"));

var user = _workContext.CurrentUser;

var order = _paymentService.CreateRechargeOrder(user.Id, money);

return Json(new PaymentResult(true) {OrderId = order.OrderNumber});

}

调用统一下单

订单创建成功之后,页面跳转到支付页面,这个时候就是按照官方的流程去拿prepay_id和paySign,微信的demo中提供了一个jsApiPay的对象。但这个对象需要一个page对象初始化。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
[LoginValid]

public ActionResult H5Pay(string orderNumber)

{

var user = _workContext.CurrentUser;

var order = _paymentService.GetOrderByOrderNumber(orderNumber);

//判断订单是否存在

//订单是否已经支付了

var openid = user.OpenId;

var jsApipay = new JsApiPayMvc(this.ControllerContext.HttpContext);

jsApipay.openid = openid;

jsApipay.total_fee = (int)order.Amount * 100;

WxPayData unifiedOrderResult = jsApipay.GetUnifiedOrderResult();

ViewBag.wxJsApiParam = jsApipay.GetJsApiParameters();//获取H5调起JS API参数

ViewBag.unifiedOrder = unifiedOrderResult.ToPrintStr();

ViewBag.OrderNumber = order.OrderNumber;

return View();

}

在MVC中我们简单改一下就可以了。也就是把page对象换成httpContext即可。然后里面的方法就可以直接用了。

JsApiPayMvc:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229
using System;

using System.Collections.Generic;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Runtime.Serialization;

using System.IO;

using System.Text;

using System.Net;

using System.Web.Security;

using LitJson;

namespace WxPayAPI

{

public class JsApiPayMvc

{

/// <summary>

/// 保存页面对象,因为要在类的方法中使用Page的Request对象

/// </summary>

public HttpContextBase context { get; set; }

/// <summary>

/// openid用于调用统一下单接口

/// </summary>

public string openid { get; set; }

/// <summary>

/// access_token用于获取收货地址js函数入口参数

/// </summary>

public string access_token { get; set; }

/// <summary>

/// 商品金额,用于统一下单

/// </summary>

public int total_fee { get; set; }

/// <summary>

/// 统一下单接口返回结果

/// </summary>

public WxPayData unifiedOrderResult { get; set; }

public JsApiPayMvc(HttpContextBase _context)

{

context = _context;

}

/**

*

* 网页授权获取用户基本信息的全部过程

* 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

* 第一步:利用url跳转获取code

* 第二步:利用code去获取openid和access_token

*

*/

public void GetOpenidAndAccessToken(string code)

{

if (!string.IsNullOrEmpty(code))

{

//获取code码,以获取openid和access_token

Log.Debug(this.GetType().ToString(), "Get code : " + code);

GetOpenidAndAccessTokenFromCode(code);

}

else

{

//构造网页授权获取code的URL

string host = context.Request.Url.Host;

string path = context.Request.Path;

string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);

WxPayData data = new WxPayData();

data.SetValue("appid", WxPayConfig.APPID);

data.SetValue("redirect_uri", redirect_uri);

data.SetValue("response_type", "code");

data.SetValue("scope", "snsapi_base");

data.SetValue("state", "STATE" + "#wechat_redirect");

string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();

Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);

try

{

//触发微信返回code码

context.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常

}

catch(System.Threading.ThreadAbortException ex)

{

}

}

}

/**

*

* 通过code换取网页授权access_token和openid的返回数据,正确时返回的JSON数据包如下:

* {

* "access_token":"ACCESS_TOKEN",

* "expires_in":7200,

* "refresh_token":"REFRESH_TOKEN",

* "openid":"OPENID",

* "scope":"SCOPE",

* "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"

* }

* 其中access_token可用于获取共享收货地址

* openid是微信支付jsapi支付接口统一下单时必须的参数

* 更详细的说明请参考网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

* @失败时抛异常WxPayException

*/

public void GetOpenidAndAccessTokenFromCode(string code)

{

try

{

//构造获取openid及access_token的url

WxPayData data = new WxPayData();

data.SetValue("appid", WxPayConfig.APPID);

data.SetValue("secret", WxPayConfig.APPSECRET);

data.SetValue("code", code);

data.SetValue("grant_type", "authorization_code");

string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();

//请求url以获取数据

string result = HttpService.Get(url);

Log.Debug(this.GetType().ToString(), "GetOpenidAndAccessTokenFromCode response : " + result);

//保存access_token,用于收货地址获取

JsonData jd = JsonMapper.ToObject(result);

access_token = (string)jd["access_token"];

//获取用户openid

openid = (string)jd["openid"];

Log.Debug(this.GetType().ToString(), "Get openid : " + openid);

Log.Debug(this.GetType().ToString(), "Get access_token : " + access_token);

}

catch (Exception ex)

{

Log.Error(this.GetType().ToString(), ex.ToString());

throw new WxPayException(ex.ToString());

}

}

/**

* 调用统一下单,获得下单结果

* @return 统一下单结果

* @失败时抛异常WxPayException

*/

public WxPayData GetUnifiedOrderResult()

{

//统一下单

WxPayData data = new WxPayData();

data.SetValue("body", "test");

data.SetValue("attach", "test");

data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());

data.SetValue("total_fee", total_fee);

data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));

data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));

data.SetValue("goods_tag", "test");

data.SetValue("trade_type", "JSAPI");

data.SetValue("openid", openid);

WxPayData result = WxPayApi.UnifiedOrder(data);

if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")

{

Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");

throw new WxPayException("UnifiedOrder response error!");

}

unifiedOrderResult = result;

return result;

}

/**

*

* 从统一下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数,

* 微信浏览器调起JSAPI时的输入参数格式如下:

* {

* "appId" : "wx2421b1c4370ec43b", //公众号名称,由商户传入

* "timeStamp":" 1395712654", //时间戳,自1970年以来的秒数

* "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串

* "package" : "prepay_id=u802345jgfjsdfgsdg888",

* "signType" : "MD5", //微信签名方式:

* "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名

* }

* @return string 微信浏览器调起JSAPI时的输入参数,json格式可以直接做参数用

* 更详细的说明请参考网页端调起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7

*

*/

public string GetJsApiParameters()

{

Log.Debug(this.GetType().ToString(), "JsApiPay::GetJsApiParam is processing...");

WxPayData jsApiParam = new WxPayData();

jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));

jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());

jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());

jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));

jsApiParam.SetValue("signType", "MD5");

jsApiParam.SetValue("paySign", jsApiParam.MakeSign());

string parameters = jsApiParam.ToJson();

Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);

return parameters;

}

/**

*

* 获取收货地址js函数入口参数,详情请参考收货地址共享接口:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_9

* @return string 共享收货地址js函数需要的参数,json格式可以直接做参数使用

*/

public string GetEditAddressParameters()

{

string parameter = "";

try

{

string host = context.Request.Url.Host;

string path = context.Request.Path;

string queryString = context.Request.Url.Query;

//这个地方要注意,参与签名的是网页授权获取用户信息时微信后台回传的完整url

string url = "http://" + host + path + queryString;

//构造需要用SHA1算法加密的数据

WxPayData signData = new WxPayData();

signData.SetValue("appid",WxPayConfig.APPID);

signData.SetValue("url", url);

signData.SetValue("timestamp",WxPayApi.GenerateTimeStamp());

signData.SetValue("noncestr",WxPayApi.GenerateNonceStr());

signData.SetValue("accesstoken",access_token);

string param = signData.ToUrl();

Log.Debug(this.GetType().ToString(), "SHA1 encrypt param : " + param);

//SHA1加密

string addrSign = FormsAuthentication.HashPasswordForStoringInConfigFile(param, "SHA1");

Log.Debug(this.GetType().ToString(), "SHA1 encrypt result : " + addrSign);

//获取收货地址js函数入口参数

WxPayData afterData = new WxPayData();

afterData.SetValue("appId",WxPayConfig.APPID);

afterData.SetValue("scope","jsapi_address");

afterData.SetValue("signType","sha1");

afterData.SetValue("addrSign",addrSign);

afterData.SetValue("timeStamp",signData.GetValue("timestamp"));

afterData.SetValue("nonceStr",signData.GetValue("noncestr"));

//转为json格式

parameter = afterData.ToJson();

Log.Debug(this.GetType().ToString(), "Get EditAddressParam : " + parameter);

}

catch (Exception ex)

{

Log.Error(this.GetType().ToString(), ex.ToString());

throw new WxPayException(ex.ToString());

}

return parameter;

}

}

}

这个页面可以在本地调试,可以比较方便的确认参数是否ok。

唤起支付

官方页面的示例如下:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 但主要的参数(mark部分)是由后台生成的,也就是上一个步骤的ViewBag.wxJsApiParam

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15
function onBridgeReady(){

WeixinJSBridge.invoke(

'getBrandWCPayRequest', {

"appId" "wx2421b1c4370ec43b", //公众号名称,由商户传入

"timeStamp"" 1395712654", //时间戳,自1970年以来的秒数

"nonceStr" "e61463f8efa94090b1f366cccfbbb444", //随机串

"package" "prepay_id=u802345jgfjsdfgsdg888",

"signType" "MD5", //微信签名方式:

"paySign" "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名

},

function(res){

if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。

}

);

}

所以在MVC中要这样写:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59
@{

ViewBag.Title = "微信支付";

Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="page" id="Wxpayment">

<div class="content">

<div>订单详情:@Html.Raw(ViewBag.unifiedOrder)</div>

<button id="h5pay" onclick="callpay()">支付</button>

</div>

<input type="hidden" value="@ViewBag.OrderNumber" id="ordernum"/>

</div>

<script type="text/javascript">

//调用微信JS api 支付

function jsApiCall() {

WeixinJSBridge.invoke(

'getBrandWCPayRequest',

@Html.Raw(ViewBag.wxJsApiParam),//josn串

function (res)

{

WeixinJSBridge.log(res.err_msg);

//alert(res.err_code + res.err_desc + res.err_msg);

if (res.err_msg == "get_brand_wcpay_request:ok") {

var num = $("#ordernum").val();

$.post("/payment/WeiXinPaySuccess", { ordernumber: num }, function(data) {

if (data.IsSuccess === true) {

alert("支付成功");

location.href = document.referrer;

} else {

}

});

}

if (res.err_msg == 'get_brand_wcpay_request:cancel') {

$('.button').removeAttr('submitting');

alert('取消支付');

}

}

);

}

function callpay()

{

if (typeof WeixinJSBridge == "undefined")

{

alert("WeixinJSBridge =");

if (document.addEventListener)

{

document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);

}

else if (document.attachEvent)

{

document.attachEvent('WeixinJSBridgeReady', jsApiCall);

document.attachEvent('onWeixinJSBridgeReady', jsApiCall);

}

}

else

{

jsApiCall();

}

}

</script>

必须要用Html.Raw,不然json解析不对,无法支付。这个时候点击页面,会出现微信的加载效果,但别高兴的太早,还是会出错,出现一个“3当前的URL未注册”

微信公众平台支付开发详解

原因就在于,需要在公众号中设置支付目录。而这个支付目录是大小写敏感的,所以你得多试几次。直到弹出输入密码的窗口才是真的流程正确了。然后支付成功之后马上就可以收到js中的回调,这个时候你可以去处理你的订单和业务逻辑。

官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

官方demo:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持快网idc!

原文链接:http://www.cnblogs.com/stoneniqiu/p/6308813.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 微信公众平台支付开发详解 https://www.kuaiidc.com/99545.html

相关文章

发表评论
暂无评论