Browse Source

新增 JSAPI下单API

Roc 5 năm trước cách đây
mục cha
commit
1ea358d71b

+ 68 - 17
samples/WebApplicationSample/Controllers/WeChatPayController.cs

@@ -602,22 +602,22 @@ namespace WebApplicationSample.Controllers
         }
 
         /// <summary>
-        /// 扫码支付-Native下单API
+        /// APP支付-App下单API
         /// </summary>
         [HttpGet]
-        public IActionResult QrCodePayV3()
+        public IActionResult AppPayV3()
         {
             return View();
         }
 
         /// <summary>
-        /// 扫码支付-Native下单API
+        /// APP支付-App下单API
         /// </summary>
         /// <param name="viewModel"></param>
         [HttpPost]
-        public async Task<IActionResult> QrCodePayV3(WeChatPayQrCodePayV3ViewModel viewModel)
+        public async Task<IActionResult> AppPayV3(WeChatPayAppPayV3ViewModel viewModel)
         {
-            var model = new WeChatPayTransactionsNativeModel
+            var model = new WeChatPayTransactionsAppModel
             {
                 AppId = _optionsAccessor.Value.AppId,
                 MchId = _optionsAccessor.Value.MchId,
@@ -627,34 +627,47 @@ namespace WebApplicationSample.Controllers
                 OutTradeNo = viewModel.OutTradeNo,
             };
 
-            var request = new WeChatPayTransactionsNativeRequest();
+            var request = new WeChatPayTransactionsAppRequest();
             request.SetBizModel(model);
 
             var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
 
-            // response.CodeUrl 给前端生成二维码
-            ViewData["qrcode"] = response.CodeUrl;
+            if (response.StatusCode == 200)
+            {
+                var req = new WeChatPayAppSdkRequest
+                {
+                    PrepayId = response.PrepayId
+                };
+
+                var parameter = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+
+                // 将参数(parameter)给 ios/android端 让他调起微信APP(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)
+                ViewData["parameter"] = JsonSerializer.Serialize(parameter);
+                ViewData["response"] = response.Body;
+                return View();
+            }
+
             ViewData["response"] = response.Body;
             return View();
         }
 
         /// <summary>
-        /// APP支付-App下单API
+        /// 公众号支付-JSAPI下单
         /// </summary>
         [HttpGet]
-        public IActionResult AppPayV3()
+        public IActionResult PubPayV3()
         {
             return View();
         }
 
         /// <summary>
-        /// APP支付-App下单API
+        /// 公众号支付-JSAPI下单
         /// </summary>
         /// <param name="viewModel"></param>
         [HttpPost]
-        public async Task<IActionResult> AppPayV3(WeChatPayAppPayV3ViewModel viewModel)
+        public async Task<IActionResult> PubPayV3(WeChatPayPubPayV3ViewModel viewModel)
         {
-            var model = new WeChatPayTransactionsAppModel
+            var model = new WeChatPayTransactionsJsApiModel
             {
                 AppId = _optionsAccessor.Value.AppId,
                 MchId = _optionsAccessor.Value.MchId,
@@ -662,23 +675,24 @@ namespace WebApplicationSample.Controllers
                 Description = viewModel.Description,
                 NotifyUrl = viewModel.NotifyUrl,
                 OutTradeNo = viewModel.OutTradeNo,
+                Payer = new Payer { OpenId = viewModel.OpenId }
             };
 
-            var request = new WeChatPayTransactionsAppRequest();
+            var request = new WeChatPayTransactionsJsApiRequest();
             request.SetBizModel(model);
 
             var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
 
             if (response.StatusCode == 200)
             {
-                var req = new WeChatPayAppSdkRequest
+                var req = new WeChatPayJsApiSdkRequest
                 {
-                    PrepayId = response.PrepayId
+                    Package = "prepay_id=" + response.PrepayId
                 };
 
                 var parameter = await _client.ExecuteAsync(req, _optionsAccessor.Value);
 
-                // 将参数(parameter)给 ios/android端 让他调起微信APP(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)
+                // 将参数(parameter)给 公众号前端 让他在微信内H5调起支付(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6)
                 ViewData["parameter"] = JsonSerializer.Serialize(parameter);
                 ViewData["response"] = response.Body;
                 return View();
@@ -687,5 +701,42 @@ namespace WebApplicationSample.Controllers
             ViewData["response"] = response.Body;
             return View();
         }
+
+        /// <summary>
+        /// 扫码支付-Native下单API
+        /// </summary>
+        [HttpGet]
+        public IActionResult QrCodePayV3()
+        {
+            return View();
+        }
+
+        /// <summary>
+        /// 扫码支付-Native下单API
+        /// </summary>
+        /// <param name="viewModel"></param>
+        [HttpPost]
+        public async Task<IActionResult> QrCodePayV3(WeChatPayQrCodePayV3ViewModel viewModel)
+        {
+            var model = new WeChatPayTransactionsNativeModel
+            {
+                AppId = _optionsAccessor.Value.AppId,
+                MchId = _optionsAccessor.Value.MchId,
+                Amount = new Amount { Total = viewModel.Total, Currency = "CNY" },
+                Description = viewModel.Description,
+                NotifyUrl = viewModel.NotifyUrl,
+                OutTradeNo = viewModel.OutTradeNo,
+            };
+
+            var request = new WeChatPayTransactionsNativeRequest();
+            request.SetBizModel(model);
+
+            var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
+
+            // response.CodeUrl 给前端生成二维码
+            ViewData["qrcode"] = response.CodeUrl;
+            ViewData["response"] = response.Body;
+            return View();
+        }
     }
 }

+ 23 - 0
samples/WebApplicationSample/Models/WeChatPayViewModel.cs

@@ -352,6 +352,29 @@ namespace WebApplicationSample.Models
         public string NotifyUrl { get; set; }
     }
 
+    public class WeChatPayPubPayV3ViewModel
+    {
+        [Required]
+        [Display(Name = "out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        [Required]
+        [Display(Name = "description")]
+        public string Description { get; set; }
+
+        [Required]
+        [Display(Name = "total")]
+        public int Total { get; set; }
+
+        [Required]
+        [Display(Name = "notify_url")]
+        public string NotifyUrl { get; set; }
+
+        [Required]
+        [Display(Name = "openid")]
+        public string OpenId { get; set; }
+    }
+
     public class WeChatPayQrCodePayV3ViewModel
     {
         [Required]

+ 6 - 0
samples/WebApplicationSample/Views/WeChatPay/Index.cshtml

@@ -140,6 +140,12 @@
         </tr>
         <tr>
             <th scope="row">21</th>
+            <td>公众号支付-JSAPI下单</td>
+            <td><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_2.shtml" target="_blank">https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi</a></td>
+            <td><a asp-controller="WeChatPay" asp-action="PubPayV3">立即测试</a></td>
+        </tr>
+        <tr>
+            <th scope="row">22</th>
             <td>扫码支付-Native下单</td>
             <td><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_3.shtml" target="_blank">https://api.mch.weixin.qq.com/v3/pay/transactions/native</a></td>
             <td><a asp-controller="WeChatPay" asp-action="QrCodePayV3">立即测试</a></td>

+ 55 - 0
samples/WebApplicationSample/Views/WeChatPay/PubPayV3.cshtml

@@ -0,0 +1,55 @@
+@model WeChatPayPubPayV3ViewModel
+@{
+    ViewData["Title"] = "公众号支付-JSAPI下单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item"><a asp-controller="WeChatPay" asp-action="Index">微信支付</a></li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<br />
+<div class="card">
+    <div class="card-body">
+        <form asp-controller="WeChatPay" asp-action="PubPayV3">
+            <div asp-validation-summary="All" class="text-danger"></div>
+            <div class="form-group">
+                <label asp-for="OutTradeNo"></label>
+                <input type="text" class="form-control" asp-for="OutTradeNo" value="@DateTime.Now.ToString("yyyyMMddHHmmssfff")">
+            </div>
+            <div class="form-group">
+                <label asp-for="Description"></label>
+                <input type="text" class="form-control" asp-for="Description" value="微信公众号支付测试">
+            </div>
+            <div class="form-group">
+                <label asp-for="Total"></label>
+                <input type="text" class="form-control" asp-for="Total" value="1">
+            </div>
+            <div class="form-group">
+                <label asp-for="NotifyUrl"></label>
+                <input type="text" class="form-control" asp-for="NotifyUrl" value="http://*/notify/wechatpay/v3/transactions">
+            </div>
+            <div class="form-group">
+                <label asp-for="OpenId"></label>
+                <input type="text" class="form-control" asp-for="OpenId">
+            </div>           <button type="submit" class="btn btn-primary">提交请求</button>
+        </form>
+        <hr />
+        <form class="form-horizontal">
+            <div class="form-group">
+                <label>QrCode:</label>
+                @if (!string.IsNullOrEmpty(ViewData["qrcode"] as string))
+                {
+                    <embed src="../Home/QrCode?size=168&data=@ViewData["qrcode"]" width="168" height="168" type="image/svg+xml" />
+                }
+            </div>
+            <div class="form-group">
+                <label>Response:</label>
+                <textarea class="form-control" rows="10">@ViewData["response"]</textarea>
+            </div>
+        </form>
+    </div>
+</div>
+@section Scripts {
+    @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 18 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/Domain/Payer.cs

@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.Domain
+{
+    /// <summary>
+    /// 支付者信息
+    /// </summary>
+    public class Payer : WeChatPayObject
+    {
+        /// <summary>
+        /// 用户标识
+        /// 用户在直连商户appid下的唯一标识。
+        /// 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+        /// </summary>
+        [JsonPropertyName("openid")]
+        public string OpenId { get; set; }
+    }
+}

+ 102 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/Domain/WeChatPayTransactionsJsApiModel.cs

@@ -0,0 +1,102 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.Domain
+{
+    /// <summary>
+    /// JSAPI下单API-请求参数
+    /// 最新更新时间:2020.05.26
+    /// https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_2.shtml
+    /// </summary>
+    public class WeChatPayTransactionsJsApiModel : WeChatPayObject
+    {
+        /// <summary>
+        /// 公众号ID
+        /// 直连商户申请的公众号或移动应用appid。
+        /// 示例值:wxd678efh567hg6787
+        /// </summary>
+        [JsonPropertyName("appid")]
+        public string AppId { get; set; }
+
+        /// <summary>
+        /// 直连商户号
+        /// 直连商户的商户号,由微信支付生成并下发。
+        /// 示例值:1230000109
+        /// </summary>
+        [JsonPropertyName("mchid")]
+        public string MchId { get; set; }
+
+        /// <summary>
+        /// 商品描述
+        /// 商品描述
+        /// 示例值:Image形象店-深圳腾大-QQ公仔
+        /// </summary>
+        [JsonPropertyName("description")]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// 商户订单号
+        /// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。
+        /// 特殊规则:最小字符长度为6
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        /// <summary>
+        /// 交易结束时间
+        /// 订单失效时间,遵循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年5月20日 13点29分35秒。
+        /// 示例值:2018-06-08T10:34:56+08:00
+        /// </summary>
+        [JsonPropertyName("time_expire")]
+        public string TimeExpire { get; set; }
+
+        /// <summary>
+        /// 附加数据
+        /// 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+        /// 示例值:自定义数据
+        /// </summary>
+        [JsonPropertyName("attach")]
+        public string Attach { get; set; }
+
+        /// <summary>
+        /// 通知地址
+        /// 通知URL必须为直接可访问的URL,不允许携带查询串。
+        /// 格式:URL
+        /// 示例值:https://www.weixin.qq.com/wxpay/pay.php
+        /// </summary>
+        [JsonPropertyName("notify_url")]
+        public string NotifyUrl { get; set; }
+
+        /// <summary>
+        /// 订单优惠标记
+        /// 订单优惠标记
+        /// 示例值:WXG
+        /// </summary>
+        [JsonPropertyName("goods_tag")]
+        public string GoodsTag { get; set; }
+
+        /// <summary>
+        /// 订单金额
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public Amount Amount { get; set; }
+
+        /// <summary>
+        /// 支付者
+        /// </summary>
+        [JsonPropertyName("payer")]
+        public Payer Payer { get; set; }
+
+        /// <summary>
+        /// 优惠功能
+        /// </summary>
+        [JsonPropertyName("detail")]
+        public Detail Detail { get; set; }
+
+        /// <summary>
+        /// 场景信息
+        /// </summary>
+        [JsonPropertyName("scene_info")]
+        public SceneInfo SceneInfo { get; set; }
+    }
+}

+ 29 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/Request/WeChatPayTransactionsJsApiRequest.cs

@@ -0,0 +1,29 @@
+using Essensoft.AspNetCore.Payment.WeChatPay.Response;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.Request
+{
+    /// <summary>
+    /// JSAPI下单API
+    /// 最新更新时间:2020.05.26
+    /// https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_2.shtml
+    /// </summary>
+    public class WeChatPayTransactionsJsApiRequest : IWeChatPayV3PostRequest<WeChatPayTransactionsJsApiResponse>
+    {
+        private WeChatPayObject bizModel;
+
+        public string GetRequestUrl()
+        {
+            return "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
+        }
+
+        public WeChatPayObject GetBizModel()
+        {
+            return bizModel;
+        }
+
+        public void SetBizModel(WeChatPayObject bizModel)
+        {
+            this.bizModel = bizModel;
+        }
+    }
+}

+ 20 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/Response/WeChatPayTransactionsJsApiResponse.cs

@@ -0,0 +1,20 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.Response
+{
+    /// <summary>
+    /// JSAPI下单API-返回参数
+    /// 最新更新时间:2020.05.26
+    /// https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_2.shtml
+    /// </summary>
+    public class WeChatPayTransactionsJsApiResponse : WeChatPayV3Response
+    {
+        /// <summary>
+        /// 预支付交易会话标识	
+        /// 预支付交易会话标识。
+        /// 示例值:wx201410272009395522657a690389285100
+        /// </summary>
+        [JsonPropertyName("prepay_id")]
+        public string PrepayId { get; set; }
+    }
+}