首先做小程序支付我们需要下面这些东西:
- appid:(小程序ID)
- app_secret:(小程序密钥)
- mch_id:(商户号)
- key:(api秘钥,商家后台自己设置)
小程序端:
做一个简单的支付页面,在index.wxml中添加一个输入框,一个button,绑定相关事件,输入框用于输入订单号,在正式使用中不需要输入框,button用于提交订单发起支付;
<input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;margin: 10rpx;height: 80rpx;"></input>
<button bindtap='pay'>支付</button>
然后,在index.js中添加以下代码,主要处理上面的绑定事件和发起支付;
Page({
data:{
orderCode:""
},
pay: function(options) {
var orderCode = this.data.orderid;
wx.login({
success: function(res) {
if (res.code) {
wx.request({
url: 'http://192.168.0.32:9003/BingAnService/Pay.do',//服务端请求地址,使用时换上自己的地址
data: {
code: res.code, //要去换取openid的登录凭证
order_code: orderCode
},
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded' // 默认值
},
success: function(res) {
console.log("支付接口返回信息:", res.data);
//拿到服务端返回数据,调用微信小程支付
wx.requestPayment({
timeStamp:res.data.timeStamp,
nonceStr:res.data.nonceStr,
package:res.data.package,
signType:res.data.signType,
paySign:res.data.paySign,
success: function(res) {//成功
console.log("支付成功:",res);
},
fail: function(res) {
console.log("支付失败:",res);
}
})
}
})
}
}
})
},
getOrderCode: function(options) {
this.setData({
orderid: options.detail.value
})
})
服务端:
我的服务端使用的是.net来做的,首先提供一个供小程序端调用的接口
public ListResultInfo Pay(JObject jObject)
{
ListResultInfo result = new ListResultInfo();
try
{
string code = jObject["code"].ToString();
string order_code = jObject["order_code"].ToString();
PayRequesEntity str = (PayRequesEntity)WXPay.Pay(code, order_code);
if (str!=null)
{
result.Error_code = 0;
result.Success = true;
result.Message = "支付结果!";
result.Total_count = 0;
result.Data = str;
}
else
{
}
}
catch (Exception e)
{
result.Error_code = 1;
result.Success = false;
result.Message = "发起支付失败!";
result.Error_desc = e.Message;
WebLog.WriteTextLog("运行", "发起支付失败:" + e.Message, DateTime.Now);
}
return result;
}
所有的支付相关的都写在WXPay这个类里面,先上代码:
public class WXPay
{
private static string APP_ID = "小程序ID";
private static string APP_SECRET = "密钥(小程序配置界面)";
private static string MCH_ID = "商户号";
private static string KEY = "api密钥(商家后台自己设置)";
private static string NOTIFY_URL = "支付回调地址";
private static string PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public static Object Pay(string code, string order_code)
{
Parameters para = new Parameters();
para.appid = APP_ID;//申请的appid
para.mchid = MCH_ID;//申请的商户号
para.nonce_str = GetNoncestr(); //随机字符串
para.notify_url = NOTIFY_URL;//支付结果回调接口
para.body = "testPay";//商品描述(商品简单描述,该字段请按照规范传递,具体请见参数规定)
para.out_trade_no = order_code;//商户订单号
para.total_fee = "0.01";//标价金额
para.spbill_create_ip = "IP地址";//终端IP
para.trade_type = "JSAPI";//交易类型
para.key = KEY;//在商家后台设置的密钥
para.app_secret = APP_SECRET;//在配置小程序时的密钥
//用code换取opendid
JObject jObject = GetOpendidAndSessionkey(code);//用户唯一标识
string opendid = string.Empty;//会话密钥
string session_key = string.Empty;
if (jObject.Property("openid") != null && jObject.Property("session_key") != null)
{
opendid = jObject["openid"].ToString();
session_key = jObject["session_key"].ToString();
}
string param = "";
if (!string.IsNullOrEmpty(opendid))
{
//获取统一的下单的请求参数
param = GetUnifiedOrderParam(opendid, para);
//统一请求下单
HttpResponseMessage hrm = (HttpResponseMessage)PostUnifiedOrder(PAY_URL, param);
if (hrm.StatusCode == HttpStatusCode.OK)
{
//拿到请求的结果
string re = hrm.Content.ReadAsStringAsync().Result;
var payRes = XDocument.Parse(re);
var root = payRes.Element("xml");
//序列化相应参数返回给小程序
var res = GetPayRequestParam(root, para.appid, para.key);
return res;
}
}
return null;
}
//用code获取opendid
public static JObject GetOpendidAndSessionkey(string code)
{
string url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + APP_ID + "&secret=" + APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
JObject JO = new JObject();
HttpClient httpClient = new HttpClient();
HttpResponseMessage hrm = httpClient.GetAsync(url).Result;
if (hrm.StatusCode == HttpStatusCode.OK)
{
string re = hrm.Content.ReadAsStringAsync().Result;
JO = (JObject)JsonConvert.DeserializeObject(re);
}
hrm.Dispose();
httpClient.Dispose();
return JO;
}
//获取统一下单的请求参数
private static string GetUnifiedOrderParam(string openid, Parameters para)
{
//参与统一下单签名的参数,除最后的key外,已经按参数名ASCII码从小到大排序
string unifiedorderSignParam = string.Format("appid={0}&body={1}&mch_id={2}&nonce_str={3}¬ify_url={4}&openid={5}&out_trade_no={6}&spbill_create_ip={7}&total_fee={8}&trade_type={9}&key={10}"
, para.appid, para.body, para.mchid, para.nonce_str, para.notify_url
, openid, para.out_trade_no, para.spbill_create_ip, para.total_fee, para.trade_type, para.key);
//MD5加密并将结果转换成大写
string unifiedorderSign = GetMD5(unifiedorderSignParam).ToUpper();
//构造统一下单的请求参数
return string.Format(@"<xml>
<appid>{0}</appid>
<body>{1}</body>
<mch_id>{2}</mch_id>
<nonce_str>{3}</nonce_str>
<notify_url>{4}</notify_url>
<openid>{5}</openid>
<out_trade_no>{6}</out_trade_no>
<spbill_create_ip>{7}</spbill_create_ip>
<total_fee>{8}</total_fee>
<trade_type>{9}</trade_type>
<sign>{10}</sign>
</xml>", para.appid, para.body, para.mchid, para.nonce_str, para.notify_url, openid
, para.out_trade_no, para.spbill_create_ip, para.total_fee, para.trade_type, unifiedorderSign);
}
//统一请求下单
private static Object PostUnifiedOrder(string payUrl, string para)
{
string result = string.Empty;
try
{
HttpClient client = new HttpClient();
HttpContent httpContent = new StringContent(para);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpContent.Headers.ContentType.CharSet = "utf-8";
HttpResponseMessage hrm = client.PostAsync(payUrl, httpContent).Result;
return hrm;
}
catch (Exception e)
{
result = e.Message;
}
return result;
}
//获取返回给小程序的支付参数
private static PayRequesEntity GetPayRequestParam(XElement root, string appid, string key)
{
//当return_code 和result_code都为SUCCESS时才有我们要的prepay_id
if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS")
{
var package = "prepay_id=" + root.Element("prepay_id").Value;//统一下单接口返回的 prepay_id 参数值
var nonceStr = GetNoncestr();//获取随机字符串
var signType = "MD5";//加密方式
var timeStamp = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString();//时间戳
var paySignParam = string.Format("appId={0}&nonceStr={1}&package={2}&signType={3}&timeStamp={4}&key={5}",
appid, nonceStr, package, signType, timeStamp, key);
//二次加签
var paySign = GetMD5(paySignParam).ToUpper();
PayRequesEntity payEntity = new PayRequesEntity
{
package = package,
nonceStr = nonceStr,
paySign = paySign,
signType = signType,
timeStamp = timeStamp
};
return payEntity;
}
return null;
}
//生成随机字符串
private static string GetNoncestr()
{
//生成随机数方法需要去按照指引下载API证书
//也可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全
//详细:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
return "";
}
//参数实体类
public class Parameters
{
public string appid;//申请的appid
public string mchid;//申请的商户号
public string nonce_str; //随机字符串
public string notify_url;//支付结果回调接口
public string body;//商品描述(商品简单描述,该字段请按照规范传递,具体请见参数规定)
public string out_trade_no;//商户订单号
public string total_fee;//标价金额
public string spbill_create_ip;//终端IP
public string trade_type;//交易类型
public string key;//在商家后台设置的密钥
public string app_secret;//在配置小程序时的密钥
}
//小程序支付需要的参数
public class PayRequesEntity
{
/// <summary>
/// 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
/// </summary>
public string timeStamp { get; set; }
/// <summary>
/// 随机字符串,长度为32个字符以下。
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 统一下单接口返回的 prepay_id 参数值
/// </summary>
public string package { get; set; }
/// <summary>
/// 签名算法
/// </summary>
public string signType { get; set; }
/// <summary>
/// 签名
/// </summary>
public string paySign { get; set; }
}
//MD5加密
public static string GetMD5(string str)
{
byte[] result = Encoding.Default.GetBytes(str);
MD5 md5 = new MD5CryptoServiceProvider();
//计算指定字节数组指定区域的哈希值
byte[] output = md5.ComputeHash(result);
StringBuilder tmp = new StringBuilder();
foreach (byte i in output)
{
tmp.Append(i.ToString("x2"));
}
return tmp.ToString();
}
}
为了方便,所以把统一下单必须的相关的参数封装到成了一个实体类Parameters(当然除了这些必须的参数还有些其他参数也可以添加具体的参考官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1)。
//参数实体类
public class Parameters
{
public string appid { get { return APP_ID; } }//申请的appid
public string mchid { get { return MCH_ID; } }//申请的商户号
public string nonce_str { get { return GetNoncestr(); } } //随机字符串
public string notify_url { get { return NOTIFY_URL; } }//支付结果回调接口
public string body { get { return "testpay"; } }//商品描述(商品简单描述,该字段请按照规范传递,具体请见参数规定)
public string out_trade_no { get; set; }//商户订单号
public string total_fee { get { return "0.01"; } }//标价金额
public string spbill_create_ip { get { return "IP地址"; } }//终端IP
public string trade_type { get { return "JSAPI"; } }//交易类型
public string key { get { return KEY; } }//在商家后台设置的密钥
public string app_secret { get { return APP_SECRET; } }//在配置小程序时的密钥
}
小程序发起请求,进到pay方法中,第一步:实例化一个参数对象Parameters,配置好相关参数,这里为了简单价格、商品描述等都是写死的,在实际中需要小程序端传过来相应的参数
public static Object Pay(string code, string order_code)
{
//1.
Parameters para = new Parameters();
para.appid = APP_ID;//申请的appid
para.mchid = MCH_ID;//申请的商户号
para.nonce_str = GetNoncestr(); //随机字符串
para.notify_url = NOTIFY_URL;//支付结果回调接口
para.body = "testPay";//商品描述(商品简单描述,该字段请按照规范传递,具体请见参数规定)
para.out_trade_no = order_code;//商户订单号
para.total_fee = "0.01";//标价金额
para.spbill_create_ip = "IP地址";//终端IP
para.trade_type = "JSAPI";//交易类型
para.key = KEY;//在商家后台设置的密钥
para.app_secret = APP_SECRET;//在配置小程序时的密钥
//2.用code换取opendid
JObject jObject = GetOpendidAndSessionkey(code);//用户唯一标识
string opendid = string.Empty;//会话密钥
string session_key = string.Empty;
if (jObject.Property("openid") != null && jObject.Property("session_key") != null)
{
opendid = jObject["openid"].ToString();
session_key = jObject["session_key"].ToString();
}
string param = "";
if (!string.IsNullOrEmpty(opendid))
{
//3.获取统一的下单的请求参数
param = GetUnifiedOrderParam(opendid, para);
//4.统一请求下单
HttpResponseMessage hrm = (HttpResponseMessage)PostUnifiedOrder(PAY_URL, param);
if (hrm.StatusCode == HttpStatusCode.OK)
{
//拿到请求的结果
string re = hrm.Content.ReadAsStringAsync().Result;
var payRes = XDocument.Parse(re);
var root = payRes.Element("xml");
//5.序列化相应参数返回给小程序
var res = GetPayRequestParam(root, para.appid, para.key);
return res;
}
}
return null;
}
第二步,根据传过来的code获取有效的opendid()
public static JObject GetOpendidAndSessionkey(string code)
{
string url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + APP_ID + "&secret=" + APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
JObject JO = new JObject();
HttpClient httpClient = new HttpClient();
HttpResponseMessage hrm = httpClient.GetAsync(url).Result;
if (hrm.StatusCode == HttpStatusCode.OK)
{
string re = hrm.Content.ReadAsStringAsync().Result;
JO = (JObject)JsonConvert.DeserializeObject(re);
}
hrm.Dispose();
httpClient.Dispose();
return JO;
}
第三步,处理统一下单参数(使用MD5进行加签,并且组装成xml)
private static string GetUnifiedOrderParam(string openid, Parameters para)
{
//参与统一下单签名的参数,除最后的key外,已经按参数名ASCII码从小到大排序
string unifiedorderSignParam = string.Format("appid={0}&body={1}&mch_id={2}&nonce_str={3}¬ify_url={4}&openid={5}&out_trade_no={6}&spbill_create_ip={7}&total_fee={8}&trade_type={9}&key={10}"
, para.appid, para.body, para.mchid, para.nonce_str, para.notify_url
, openid, para.out_trade_no, para.spbill_create_ip, para.total_fee, para.trade_type, para.key);
//MD5加密并将结果转换成大写
string unifiedorderSign = GetMD5(unifiedorderSignParam).ToUpper();
//构造统一下单的请求参数
return string.Format(@"<xml>
<appid>{0}</appid>
<body>{1}</body>
<mch_id>{2}</mch_id>
<nonce_str>{3}</nonce_str>
<notify_url>{4}</notify_url>
<openid>{5}</openid>
<out_trade_no>{6}</out_trade_no>
<spbill_create_ip>{7}</spbill_create_ip>
<total_fee>{8}</total_fee>
<trade_type>{9}</trade_type>
<sign>{10}</sign>
</xml>", para.appid, para.body, para.mchid, para.nonce_str, para.notify_url, openid
, para.out_trade_no, para.spbill_create_ip, para.total_fee, para.trade_type, unifiedorderSign);
}
第四步,发起post请求,调用统一下单接口
private static Object PostUnifiedOrder(string payUrl, string para)
{
string result = string.Empty;
try
{
HttpClient client = new HttpClient();
HttpContent httpContent = new StringContent(para);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpContent.Headers.ContentType.CharSet = "utf-8";
HttpResponseMessage hrm = client.PostAsync(payUrl, httpContent).Result;
return hrm;
}
catch (Exception e)
{
result = e.Message;
}
return result;
}
第五步,将请求结果,序列化相应参数返回给小程序
private static PayRequesEntity GetPayRequestParam(XElement root, string appid, string key)
{
//当return_code 和result_code都为SUCCESS时才有我们要的prepay_id
if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS")
{
var package = "prepay_id=" + root.Element("prepay_id").Value;//统一下单接口返回的 prepay_id 参数值
var nonceStr = GetNoncestr();//获取随机字符串
var signType = "MD5";//加密方式
var timeStamp = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString();//时间戳
var paySignParam = string.Format("appId={0}&nonceStr={1}&package={2}&signType={3}&timeStamp={4}&key={5}",
appid, nonceStr, package, signType, timeStamp, key);
//二次加签
var paySign = GetMD5(paySignParam).ToUpper();
//将加签好的数据放入实体中
PayRequesEntity payEntity = new PayRequesEntity
{
package = package,
nonceStr = nonceStr,
paySign = paySign,
signType = signType,
timeStamp = timeStamp
};
return payEntity;
}
return null;
}
将序列化好的参数返回给小程序;
支付成功后,微信服务会给我们发送一个异步回调,调用的是我们上面填写的回调地址
//支付成功后的异步回调
public void Notifyurl()
{
//在这里处理你的支付成功后的逻辑
}