在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言:最近做了App的支付功能,虽然最后的代码只有巴掌大,但是踩坑的经历真的是异常丰富,问就是官方坑爹的版本升级和core少的可怜的参考资料(望天)。 阅读本文章您将收获到:1.微信和支付宝支付的保姆级后端教程 2.官方文档解析 3.独家的踩坑提示 4.一个本地可运行的demo 注意事项:1. 仅后端代码部分,不涉及任何前端 2.仅APP支付 代码demo: https://files-cdn.cnblogs.com/files/rulasann/PayUtil.zip (已重新上传)
1.官方文档: https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay 2.参数说明(请结合官方文档阅读): 有两种参数类型,公共参数和请求参数,如果调用官方的SDK,公共参数中的sign不需要自己签。例: IAopClient client = new DefaultAopClient("https://openapi.alipay.com/gateway.do", "app_id", "merchant_private_key", "json", "1.0", "RSA2", "alipay_public_key", "GBK", false); 公共参数中的biz_content即为请求参数,这里可不参照官方给的示例写,他们有封装好的model。例: //以下为发起请求的最简参数 关于notify_url,非必填,如果需要,可以这样写: request.SetNotifyUrl(notify_url) 3.完整请求示例,需Nuget引用AlipaySDKNet.Standard,其次建议用一个实体类封装配置信息: 1 public class AliPayBasicItem 2 { 3 /// <summary> 4 /// 开发者的应用ID , 必填 5 /// </summary> 6 public static string app_id = ""; 7 8 /// <summary> 9 /// 请求使用的编码格式 10 /// </summary> 11 public static string charset = "utf-8"; 12 13 /// <summary> 14 /// 仅支持"JSON",非必填 15 /// </summary> 16 public static string format = "json"; 17 18 /// <summary> 19 /// 签名算法 20 /// </summary> 21 public static string sign_type = "RSA2"; 22 23 /// <summary> 24 /// 调用的接口版本 25 /// </summary> 26 public static string version = "1.0"; 27 28 /// <summary> 29 /// 支付宝请求url 30 /// </summary> 31 public static string url = "https://openapi.alipay.com/gateway.do"; 32 33 /// <summary> 34 /// 商户私钥 (必填) 35 /// </summary> 36 public static string merchant_private_key = ""; 37 38 /// <summary> 39 /// 支付宝公钥 (必填) 40 /// </summary> 41 public static string alipay_public_key = ""; 42 43 /// <summary> 44 /// 支付完成后的通知地址 非必填 45 /// </summary> 46 public static string pay_notify_url = ""; 47 48 /// <summary> 49 /// 币种 50 /// </summary> 51 public static string currency = "CNY"; 52 53 /// <summary> 54 /// 退款完成后的通知地址 非必填 55 /// </summary> 56 //public static string refund_notify_url = ""; 57 58 /// <summary> 59 /// 应用名称 60 /// </summary> 61 public static string app_name = "mc"; 62 63 /// <summary> 64 /// 签约号 (必填) 65 /// </summary> 66 public static string pid = ""; 67 } 1 public string AppPay() 2 { 3 IAopClient client = new DefaultAopClient(AliPayBasicItem.url, AliPayBasicItem.app_id, AliPayBasicItem.merchant_private_key, AliPayBasicItem.format, AliPayBasicItem.version, AliPayBasicItem.sign_type, AliPayBasicItem.alipay_public_key, AliPayBasicItem.charset, false); 4 AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); 5 6 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 7 model.TotalAmount = "0.01"; // 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 8 //model.Body = ""; // 商品描述 9 model.Subject = "交易标题"; // 商品标题/交易标题/订单标题/订单关键字等 10 model.OutTradeNo = ""; // 商户订单号,由商家自定义,需保证商家系统中唯一。仅支持数字、字母、下划线 11 model.ProductCode = "QUICK_MSECURITY_PAY"; // 销售产品码,商家和支付宝签约的产品码。QUICK_MSECURITY_PAY:App支付。 12 request.SetBizModel(model); // 将业务model载入到request 13 //request.SetNotifyUrl(AliPayBasicItem.pay_notify_url); 14 15 AlipayTradeAppPayResponse response = client.SdkExecute(request); 16 var info = response.Body; 17 18 return info // 将这里的info直接返给前端 19 } 4.响应: 有坑注意:官方给的响应示例是个json,实际上我们拿到的(即上面示例中的info)并不长这样,如下图所示,是加密过的,看不懂没关系,不需要处理,直接返给前端就好,由前端拿着那长串去唤起支付,至此支付过程结束。 5.支付结果处理: ① 如果notify_url 赋了值,支付宝会将支付结果post给这个接口,处理一下接收数据 ② 可主动调用查询接口去获取支付结果 https://opendocs.alipay.com/apis/api_1/alipay.trade.query ,写法跟支付类似,封装的model为AlipayTradeQueryModel
part2:微信App支付1.官方文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml 有坑注意:微信支付接口已经升级到v3版本,网上找的示例可能还是老版本,区别是,老版本为MD5加密,参数格式为XML,新版本为SHA2加密,参数格式为json 2.参数说明: 官方给的请求参数如下图,没什么好说的,就一个json:
*金额total是int类型; *notify_url与支付宝不同,微信是必填,官方建议是https且无端口号,实际上http加有端口号也是可以的(不建议哈); 3.签名: 需手动对接口参数签名,签名过程参考:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml ,以下以微信支付为例: ①拼接待签名字符串:请求方法\n+URL\n+时间戳\n+随机字符串\n+请求参数\n TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); string time_str = Convert.ToInt64(ts.TotalSeconds).ToString(); string sign_url = "/v3/pay/transactions/app"; string nonce_str = Guid.NewGuid().ToString().Replace("-", ""); string body_str = ""; // 上面的参数 string to_sign = "POST" + "\n" + sign_url + "\n" + time_str + "\n" + nonce_str + "\n" + body_str + "\n"; ②使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值 byte[] keyData = Convert.FromBase64String(key); using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob)) using (RSACng rsa = new RSACng(cngKey)) { byte[] data = System.Text.Encoding.UTF8.GetBytes(to_sign); string sign = Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } ③设置请求头:Authorization: 认证类型 签名信息 认证类型为:WECHATPAY2-SHA256-RSA2048 签名信息为:商户号mchid+商户API证书serial_no+请求随机串nonce_str+时间戳timestamp+签名值sign string value = $"WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + mch_id + "\",serial_no=\"" + serial_no + "\",nonce_str=\"" + nonce_str + "\",timestamp=\"" + time_str + "\",signature=\"" + sign + "\""; request.Headers.Add("Authorization", value); 有坑注意:请求头还需有以下设置,不然会报400 request.ContentType = "application/json;charset=utf-8"; request.Headers.Add("Accept", "application/json"); request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); 4.返回值处理: 请参考https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml; 以上是前端调起支付要用到的字段,由后端通过返回值拼接处理而成 5.完整代码示例: ①要用到的实体类 1 public class WxPayBasicItem 2 { 3 /// <summary> 4 /// 小程序ID 5 /// </summary> 6 public static string appid = ""; 7 8 /// <summary> 9 /// 商户号 10 /// </summary> 11 public static string mch_id = ""; 12 13 /// <summary> 14 /// 商户证书序列号 15 /// </summary> 16 public static string serial_no = ""; 17 18 /// <summary> 19 /// 商户私钥 20 /// </summary> 21 public static string private_key = ""; 22 23 /// <summary> 24 /// 小程序支付请求url 25 /// </summary> 26 //public static string order_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; 27 28 /// <summary> 29 /// app支付请求url 30 /// </summary> 31 public static string app_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app"; 32 33 /// <summary> 34 /// 预支付完成后的通知地址 35 /// </summary> 36 public static string order_notify_url = "https://weixin.qq.com/"; 37 38 /// <summary> 39 /// 交易类型 小程序取值"JSAPI" 40 /// </summary> 41 //public static string trade_type = "JSAPI"; 42 43 /// <summary> 44 /// 查询订单请求url 45 /// </summary> 46 public static string query_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/id"; 47 48 /// <summary> 49 /// 应用密钥 50 /// </summary> 51 //public static string secret = ""; 52 53 /// <summary> 54 /// 授权登录url 55 /// </summary> 56 public static string auth_login_url = "https://api.weixin.qq.com/sns/oauth2/access_token"; 57 58 /// <summary> 59 /// 微信获取用户信息url 60 /// </summary> 61 public static string wx_user_url = "https://api.weixin.qq.com/sns/userinfo"; 62 } 63 64 /// <summary> 65 /// App预支付参数 66 /// </summary> 67 public class AppPaymentData 68 { 69 /// <summary> 70 /// 小程序ID 71 /// </summary> 72 //[Required] 73 public string appid { get; set; } 74 75 /// <summary> 76 /// 商户号 77 /// </summary> 78 public string mchid { get; set; } 79 80 /// <summary> 81 /// 商品描述 商品简单描述,不超过128字节 82 /// </summary> 83 public string description { get; set; } 84 85 /// <summary> 86 /// 商户订单号 87 /// </summary> 88 public string out_trade_no { get; set; } 89 90 /// <summary> 91 /// 交易结束时间 yyyyMMddHHmmss,非必填 92 /// </summary> 93 //public string time_expire { get; set; } 94 95 /// <summary> 96 /// 附加数据 非必填 97 /// </summary> 98 //public string attach { get; set; } 99 100 /// <summary> 101 /// 异步通知地址 通知url必须为外网可访问的url,不能携带参数。 102 /// </summary> 103 public string notify_url { get; set; } 104 105 /// <summary> 106 /// 订单优惠标记 107 /// </summary> 108 //public string goods_tag { get; set; } 109 110 /// <summary> 111 /// 订单金额信息 112 /// </summary> 113 public PaymentDataAmount amount { get; set; } 114 115 /// <summary> 116 /// 场景信息 117 /// </summary> 118 //public PaymentDataScene scene_info { get; set; } 119 120 /// <summary> 121 /// 结算信息 122 /// </summary> 123 //public PaymentDataSettle settle_info { get; set; } 124 } 125 126 /// <summary> 127 /// 订单金额 128 /// </summary> 129 public class PaymentDataAmount 130 { 131 /// <summary> 132 /// 总金额 订单总金额,单位为分 133 /// </summary> 134 public int total { get; set; } 135 136 /// <summary> 137 /// 货币类型 非必填,默认"CNY" 138 /// </summary> 139 public string currency { get; set; } 140 } 141 142 /// <summary> 143 /// App调起支付参数 144 /// </summary> 145 public class AppPayBackItem 146 { 147 /// <summary> 148 /// 应用id 149 /// </summary> 150 public string appid { get; set; } 151 152 /// <summary> 153 /// 商户号 154 /// </summary> 155 public string partnerid { get; set; } 156 157 /// <summary> 158 /// 预支付交易会话ID 159 /// </summary> 160 public string prepayid { get; set; } 161 162 /// <summary> 163 /// 订单详情扩展字符串 164 /// </summary> 165 public string package { get; set; } 166 167 /// <summary> 168 /// 随机字符串 169 /// </summary> 170 public string noncestr { get; set; } 171 172 /// <summary> 173 /// 时间戳 174 /// </summary> 175 public string timestamp { get; set; } 176 177 /// <summary> 178 /// 签名 179 /// </summary> 180 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论