商户或企业前往微信开放平台申请开通微信支付,需要注意的是微信支付功能不对个人用户开放。开通支付功能后,会获取到以下微信支付相关的配置信息:
- appid:是微信公众账号或开放平台APP的唯一标识,在公众平台申请公众账号或者在开放平台申请APP账号后,微信会自动分配对应的appid,用于标识该应用。
- mch_id:商户申请微信支付后,由微信支付分配的商户收款账号。
- key:交易过程用于生成签名的密钥,可以前往:微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>API安全–>密钥设置
- appsecret:AppSecret是APPID对应的接口密码,用于获取接口调用凭证access_token时使用,不参与微信支付过程。
一、支付流程
二、协议规则
使用微信API接口时,必须遵循以下规则:
- 传输方式:为保证交易安全性,采用HTTPS传输
- 提交方式:采用POST方法提交
- 数据格式:提交和返回数据都为XML格式,根节点名为xml
- 字符编码:微信支付API v2仅支持UTF-8字符编码。
- 签名算法:MD5/HMAC-SHA256
三、签名算法
微信支付使用的是MD5/HMAC-SHA256对数据进行签名和验证。
首先将所有发送或接受的非空数据按URL键值对(key1=value1&key2=value2)的形式,按参数名ASCII码从小到大排序(字典序)拼接成字符串,然后将用于微信签名的key拼接在最后,最后使用MD5/HMAC-SHA256对字符串签名得到sign值。需要注意的是:
- 参数必须按ASCII码从小到大排序。
- 为空参数不参与签名。
- 参数名区分大小写。
- 验证调签名时,sign参数不参与签名。将生成的签名与该sign值作校验。
四、异步通知
微信后台支付成功后,会调用商户后台的异步通知接口,将支付结果发送给商户后台。商户后台根据获取到的支付结果,处理业务平台逻辑。在处理时需要注意一下几点:
- 同样的异步通知微信可能发送多次,必须采用数据锁进行并发控制,避免数据混乱。
- 商户后台在处理通知时,需要先判断该通知是否已经处理成功。如果处理成功,直接返回成功信息。
- 微信后台没有接受到通知接口返回的成功信息,会按照一定的频率重新发送通知,知道成功为止(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)。
五、集成SDK
前往微信开放平台下载 SDK与DEMO ,将Demo中的business、lib放入项目中使用。
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using QRCoder;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using WxPayAPI;
using Microsoft.Extensions.Logging;
using System.Text;
namespace WxPay.Demo.Controllers
{
public class PayController : Controller
{
private readonly ILogger<HomeController> _logger;
public PayController(ILogger<HomeController> logger)
{
_logger = logger;
}
/// <summary>
/// 生成直接支付url,支付url有效期为2小时。
/// </summary>
/// <returns></returns>
public IActionResult Order()
{
WxPayData data = new WxPayData();
data.SetValue("body", "测试商品");//商品描述
data.SetValue("attach", "test");//附加数据
data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//商户订单号,随机字符串。
data.SetValue("total_fee", Convert.ToInt32(0.01 * 100));//订单总金额,单位为分。
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
data.SetValue("goods_tag", "jjj");//商品标记
data.SetValue("trade_type", "NATIVE");//交易类型
data.SetValue("product_id", Guid.NewGuid().ToString("N"));//商品ID
WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
_logger.LogInformation("UnifiedOrder:\r\n" + result.ToXml());
string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
//使用QRCoder生成二维码
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
var qrCode = new QRCode(qrCodeData);
var image = qrCode.GetGraphic(6);
using (var ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Jpeg);
return File(ms.ToArray(), "image/jpeg");
}
}
private static object _locker = new object();
public IActionResult Notify()
{
//1.签名验证
//2.判断订单是否已经处理,如果处理直接返回成功。
//3.处理业务平台订单
//4.向微信后台 返回处理结果。
WxPayData res = new WxPayData();
lock (_locker)
{
var body = HttpContext.Request.BodyReader.AsStream();
int count = 0;
byte[] buffer = new byte[1024];
StringBuilder builder = new StringBuilder();
while ((count = body.Read(buffer, 0, 1024)) > 0)
{
builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
}
var xml = builder.ToString();
_logger.LogInformation("Notify:\r\n" + xml);
try
{
//转换数据格式并验证签名
WxPayData data = new WxPayData();
var dics = data.FromXml(xml.ToString());
if (dics["return_code"] == "SUCCESS")
{
//处理业务平台订单....
var appid = dics["appid"];//公众账号ID
var mch_id = dics["mch_id"];//商户号
var out_trade_no = dics["out_trade_no"];//商户订单号
var total_fee = dics["total_fee"];//订单总金额,单位为分。
var time_end = dics["time_end"];//支付完成时间
var transaction_id = dics["transaction_id"];//微信支付订单号
res.SetValue("return_code", "SUCCESS");//返回状态码:SUCCESS/FAIL
res.SetValue("return_msg", "");//返回信息
}
else
{
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "");
}
}
catch (WxPayException ex)
{
//若签名错误,则立即返回结果给微信支付后台
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", ex.Message);
}
}
return Content(res.ToXml(), "text/xml");
}
}
}