1. 程式人生 > 其它 >Asp.Net Core3.0 微信小程式統一下單、退款、轉賬

Asp.Net Core3.0 微信小程式統一下單、退款、轉賬

微信統一下單開發文件:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

微信支付小程式支付文件:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml

微信支付申請退款文件:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_8_11.shtml

微信支付付款到零錢開發文件:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

簡單記錄一下net core3.0對接微信支付的相關程式碼,網上有很多微信支付的文章,但是一定要仔細看一遍微信的官方文件。

準備工作:

首先要開通小程式的支付能力,按要求提交稽核材料,稽核通過後,我們可以得到APPID、微信支付商戶號mch_id、API金鑰key、Appsecret,詳見 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=3_1

之後在商戶後臺繫結同一主體的APPID並授權,發起授權後,商戶需要自行前往對應平臺確認授權申請。

最後在商戶後臺設定回撥地址。

一、微信支付

1.支付V3

//支付介面 
 public async Task<ResponseResult<dynamic>> unifiedorder(int money)
{
  var
url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; var req = new GenerateOrderModelForWxPay { appid = _appid, mchid = _mchid, description = "XXXXX支付交易單", amount = new WxPayAmountModel { total
= money, currency = "CNY" }, payer = new WxPayerModel { openid = "付款人的openid" }, out_trade_no = DateTime.Now.ToString("yyyyMMddhhmmss"), notify_url = "http://api.xxxx.com/api/pay/NotifyUrl" //支付回撥,自己建立 }; _log.WriteLog(JsonConvert.SerializeObject(req)); var httpHandler = new HttpHandler(_mchid, _serialNo); HttpClient client = new HttpClient(httpHandler); var bodyJson = new StringContent(JsonConvert.SerializeObject(req), Encoding.UTF8, "application/json"); //一定要這樣傳遞引數,不然在加密簽名的時候獲取到的引數就是\\u0這種形式的資料了,不是傳遞的這樣的資料了,導致加密的結果不正確 var response = await client.PostAsync(url, bodyJson); var respStr = await response.Content.ReadAsStringAsync();//讀取統一下單之後的返回結果,這樣讀取出來的直接就是結果,或者錯誤原因。這裡面就包含prepay_id了

        var timeStamp = DateTimeOffset.Now.ToUnixTimeSeconds();
        var nonceStr = Path.GetRandomFileName();
        string message = $"{_appid}\n{timeStamp}\n{nonceStr}\nprepay_id={JsonConvert.DeserializeObject<dynamic>(respStr).prepay_id}\n";
        string signature = httpHandler.Sign(message);
        var newObj = new
        {
        timeStamp= timeStamp.ToString(),
        nonceStr = nonceStr,
        package= "prepay_id="+JsonConvert.DeserializeObject<dynamic>(respStr).prepay_id,
        paySign = signature
        };
        return new ResponseResult<dynamic>()
        {
        Code = Constant.Success,
        Data=JsonConvert.SerializeObject(newObj),
        };

}

//model

public class WxPayAmountModel

{
  public int total { get; set; }
  public string currency { get; set; }
}

public class WxPayerModel
{
  public string openid { get; set; }
}

/// <summary>
/// 支付resource
/// </summary>
public class GenerateOrderModelForWxPay
{
  public string appid { get; set; }
  public string mchid { get; set; }
  public string description { get; set; }
  public WxPayAmountModel amount { get; set; }
  public WxPayerModel payer { get; set; }
  public string out_trade_no { get; set; }
  public string notify_url { get; set; }
}

支付完成後,微信返回的結果格式如下:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

2.支付結果通知:

支付完成後,微信會把相關支付結果及使用者資訊通過資料流的形式傳送給商戶,商戶需要接收處理,並按文件規範返回應答。

通知頻率為15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 總計 24h4m)這裡通知傳送可能會多臺伺服器進行傳送,且傳送時間可能會在幾秒內,但微信不保證通知最終一定能成功。

支付成功後,微信會將結果通知商戶後臺,即支付程式碼裡的notify_url連結,該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。

微信呼叫回撥地址返回資料格式:
<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付測試]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
  <coupon_fee><![CDATA[10]]></coupon_fee>
  <coupon_count><![CDATA[1]]></coupon_count>
  <coupon_type><![CDATA[CASH]]></coupon_type>
  <coupon_id><![CDATA[10000]]></coupon_id>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>

獲取到微信的通知後,商戶處理業務邏輯後要同步返回給微信結果,SUCCESS表示商戶接收通知成功並校驗成功。

 public dynamic NotifyUrl(NotifyDto ret)
        {
            WxPayAPI.WxPayData res = new WxPayAPI.WxPayData();
            res.SetValue("return_code", "FAIL");
            res.SetValue("return_msg", "訂單查詢失敗");

            try
            {
                if (ret.Event_type == "TRANSACTION.SUCCESS")//支付成功
                {
                    //解密資料報文
                    var dataJson = AesGcmHelper.AesGcmDecryptFromBase64(_key, ret.Resource.Nonce, ret.Resource.Ciphertext, ret.Resource.Associated_data);
                    //轉換物件接受
                    var data = JsonConvert.DeserializeObject<PayCipherText>(dataJson);
                    //處理自己的業務邏輯
                     TODO...

                    res.SetValue("return_code", "SUCCESS");
                    res.SetValue("return_msg", "OK");
                    return Content(res.ToXml());
                }
                else
                {
                    _log.WriteLog("NotifyFaile:" + JsonConvert.SerializeObject(ret));

                }
                return Content(res.ToXml());
            }
            catch (Exception ex)
            {
                _log.WriteLog(ex.ToString());
                return Content(res.ToXml());
            }
        }

/// 支付結果回撥接收引數
public class NotifyDto
{
/// 通知ID通知的唯一ID
/// 示例值:EV-2018022511223320873
public string Id { get; set; }
/// 通知建立時間 通知建立的時間,遵循rfc3339標準格式,格式為YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出現在字串中,表示time元素的開頭,HH:mm:ss.表示時分秒,TIMEZONE表示時區(+08:00表示東八區時間,領先UTC 8小時,即北京時間)。例如:2015-05-20T13:29:35+08:00表示北京時間2015年05月20日13點29分35秒。
/// 示例值:2015-05-20T13:29:35+08:00
public string Create_time { get; set; }
/// 通知型別 通知的型別,支付成功通知的型別為TRANSACTION.SUCCESS
/// 示例值:TRANSACTION.SUCCESS
public string Event_type { get; set; }
/// 通知資料型別 通知的資源資料型別,支付成功通知為encrypt-resource
/// 示例值:encrypt-resource
public string Resource_type { get; set; }
/// 通知資料 通知資源資料
/// json格式,見示例
public Resource Resource { get; set; }
/// 回撥摘要
/// 示例值:支付成功

public string Summary { get; set; }
}

//回報文model

public class PayCipherText
{
public string out_trade_no { get; set; }
public string transaction_id { get; set; }
public string trade_type { get; set; }
public string trade_state { get; set; }
public string trade_state_desc { get; set; }
public string bank_type { get; set; }
public string success_time { get; set; }
public WxPayAmountModel amount { get; set; }
}

這裡用到證書和回撥報文解密,參考https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi

二、退款

1.申請退款V3

請求URL:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds

請求方式:POST

當交易發生之後一年內,由於買家或者賣家的原因需要退款時,賣家可以通過退款介面將支付金額退還給買家,微信支付將在收到退款請求並且驗證成功之後,將支付款按原路退還至買家賬號上。

 
//微信退款
public async Task Refund() { //微信退費操作 var url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; int money = 0; var retParamar = new { //transaction_id = "", out_trade_no = "", out_refund_no = "", reason = "客戶申請提現,小程式退款", amount = new { refund = money, total = money, currency = "CNY" }, notify_url = "http://api.xxxx.com/api/pay/RefundNotifyUrl" //回撥地址 }; var reqJson = JsonConvert.SerializeObject(retParamar); var httpHandler = new HttpHandler(_mchid, _serialNo); HttpClient client = new HttpClient(httpHandler); var bodyJson = new StringContent(reqJson, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, bodyJson); var respStr = await response.Content.ReadAsStringAsync(); var respResult = JsonConvert.DeserializeObject<dynamic>(respStr);
      //處理自己業務邏輯
      TODO...
}

2.退款回撥V3

  public dynamic RefundNotifyUrl(NotifyDto ret)
        {
            WxPayAPI.WxPayData res = new WxPayAPI.WxPayData();
            res.SetValue("return_code", "FAIL");
            res.SetValue("return_msg", "失敗");
            try
            {
                if (ret.Event_type == "REFUND.SUCCESS")//成功
                {
                    //解密資料報文
                    var dataJson = AesGcmHelper.AesGcmDecryptFromBase64(_key, ret.Resource.Nonce, ret.Resource.Ciphertext, ret.Resource.Associated_data);
                    //轉換物件接受
                    var data = JsonConvert.DeserializeObject<RefundCipherText>(dataJson);
                   //處理自己邏輯
            TODO...
res.SetValue("return_code", "SUCCESS"); res.SetValue("return_msg", "成功"); return Content(res.ToXml()); } else { _log.WriteLog("NotifyFaile:" + JsonConvert.SerializeObject(ret)); } return Content(res.ToXml()); } catch (Exception ex) { _log.WriteLog(ex.ToString()); return Content(res.ToXml()); } }

/// <summary>
/// 退款resource
/// </summary>
public class RefundCipherText
{
  public string mchid { get; set; }
  public WxRefundAmountModel amount { get; set; }
  public string out_trade_no { get; set; }
  public string transaction_id { get; set; }
  public string refund_id { get; set; }
  public string refund_status { get; set; }
  public string user_received_account { get; set; }
  public string success_time { get; set; }

}

 

 三、微信轉賬到零錢

TODO...