.NET Core之微信支付之公众号、H5支付详解

前言

本篇主要记录微信支付中公众号及H5支付全过程。

准备篇

公众号或者服务号(并开通微信支付功能)、商户平台中开通JSAPI支付、H5支付。

配置篇

公众号或者服务号中 -------开发-------开发者工具---------web开发者工具-------绑定为开发者

公众号或者服务号中 -------公众号设置--------功能设置   :填写业务域名、JS安全域名、网页授权域名 示例:pay.one.com

商户平台中--------产品中心-------开发配置------JSAPI支付授权目录填写:http://pay.one.com/    http://pay.one.com/WeChatPay/PubPay/-----H5支付填写:pay.one.com

若对配置还有疑问,可参考官方文档:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

开发篇

JSAPI支付

本Demo是基于Payment 的SDK开发。具体详情可参考: https://github.com/Essensoft/Payment

首先 使用Nuget安装payment:

Install-Package  :Essensoft.AspNetCore.Payment.WeChatPay -Version 2.3.2

建一个Model: WeChatPayPubPayViewModel

public class WeChatPayPubPayViewModel
  {
    [Required]
    [Display(Name = "out_trade_no")]
    public string OutTradeNo { get; set; }

    [Required]
    [Display(Name = "body")]
    public string Body { get; set; }

    [Required]
    [Display(Name = "total_fee")]
    public int TotalFee { get; set; }

    [Required]
    [Display(Name = "spbill_create_ip")]
    public string SpbillCreateIp { get; set; }

    [Required]
    [Display(Name = "notify_url")]
    public string NotifyUrl { get; set; }

    [Required]
    [Display(Name = "trade_type")]
    public string TradeType { get; set; }

    [Required]
    [Display(Name = "openid")]
    public string OpenId { get; set; }
  }

WeChatPayController:

//微信支付请求客户端(用于处理请求与响应)
private readonly IWeChatPayClient _client;
private readonly ILogger<WeChatPayController> _logger;

 private IHttpContextAccessor _accessor;

public WeChatPayController(IWeChatPayClient client, IHttpContextAccessor accessor, ILogger<WeChatPayController> logger)
    {
      _client = client;
      _accessor = accessor;
      _logger = logger;
    }
    /// <summary>
    /// 公众号支付
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public IActionResult PubPay()
    {
      WeChatPayPubPayViewModel payModel=new WeChatPayPubPayViewModel()
      {
        Body = "微信公众号支付测试",
        OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
        TotalFee = 1,//分 单位
        SpbillCreateIp = "127.0.0.1",
        NotifyUrl = "http://pay.one.com/notify/wechatpay/unifiedorder",
        TradeType = "JSAPI",
        OpenId = "" //此处需进行授权 获取OpenId
      };
      return View(payModel);
    }

    /// <summary>
    /// 公众号支付
    /// </summary>
    /// <param name="viewModel"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<IActionResult> PubPay(WeChatPayPubPayViewModel viewModel)
    {
      if(string.IsNullOrEmpty(viewModel.OpenId))
      {
        ViewData["response"] = "请返回上级重新进入此页面以获取最新数据";
        return View();
      }

      var request = new WeChatPayUnifiedOrderRequest
      {
        Body = viewModel.Body,
        OutTradeNo = viewModel.OutTradeNo,
        TotalFee = viewModel.TotalFee,
        SpbillCreateIp = viewModel.SpbillCreateIp,
        NotifyUrl = viewModel.NotifyUrl,
        TradeType = viewModel.TradeType,
        OpenId = viewModel.OpenId //此处需进行授权 获取OpenId
      };
      var response = await _client.ExecuteAsync(request);if (response.ReturnCode == "SUCCESS" && response.ResultCode == "SUCCESS")
      {
        var req = new WeChatPayH5CallPaymentRequest
        {
          Package = "prepay_id=" + response.PrepayId
        };
        var parameter = await _client.ExecuteAsync(req);
        // 将参数(parameter)给 公众号前端 让他在微信内H5调起支付(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6)
        ViewData["parameter"] = JsonConvert.SerializeObject(parameter);
        ViewData["response"] = response.Body;
        return View();
      }
      ViewData["response"] = response.Body;
      return View();
    }

注意:公众号或者微信内支付,需要授权获取到用户的OpenId。所以,此处我们还需要进行微信授权,而授权方式有两种,一种是静默授权、一种是需要用户同意,区别是 静默授权只能拿到Openid,而经用户同意后可拿到 微信头像、昵称、性别等其他信息。

具体可参阅文档: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

页面:

@using Newtonsoft.Json
@model WeChatPayPubPayViewModel
@{
  ViewData["Title"] = "公众号支付-统一下单";
}
<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="PubPay">
      <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="@Model?.OutTradeNo" />
      </div>
      <div class="form-group">
        <label asp-for="Body"></label>
        <input type="text" class="form-control" asp-for="Body" value="@Model?.Body" />
      </div>
      <div class="form-group">
        <label asp-for="TotalFee"></label>
        <input type="text" class="form-control" asp-for="TotalFee" value="@Model?.TotalFee" />
      </div>
      <div class="form-group">
        <label asp-for="SpbillCreateIp"></label>
        <input type="text" class="form-control" asp-for="SpbillCreateIp" value="@Model?.SpbillCreateIp" />
      </div>
      <div class="form-group">
        <label asp-for="NotifyUrl"></label>
        <input type="text" class="form-control" asp-for="NotifyUrl" value="@Model?.NotifyUrl" />
      </div>
      <div class="form-group">
        <label asp-for="TradeType"></label>
        <input type="text" class="form-control" asp-for="TradeType" value="@Model?.TradeType" />
      </div>
      <div class="form-group">
        <label asp-for="OpenId"></label>
        <input type="text" class="form-control" asp-for="OpenId" value="@Model?.OpenId" />
      </div>
      <button type="submit" class="btn btn-primary">提交请求</button>
      <button type="button" class="btn btn-success" id="PayNow">立即支付</button>
    </form>
    <hr />
    <form class="form-horizontal">
      <div class="form-group">
        <label>Response:</label>
        <textarea class="form-control" rows="10">@ViewData["response"]</textarea>
      </div>
      <div class="form-group">
        <label>Parameter:</label>
        <textarea class="form-control" rows="3">@ViewData["parameter"]</textarea>
      </div>
    </form>
  </div>
</div>
@section Scripts {
  @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script type="text/javascript">
  $(function () {
    $("#PayNow").on('click', function () {
      const local = "http://pay.one.com/WeChatPay/PayBack/";
       window.location.href ='https://open.weixin.qq.com/connect/oauth2/authorize?appid=@ViewBaig.AppId&redirect_uri=' + encodeURIComponent(local)+'&response_type=code&scope=snsapi_base&state=a#wechat_redirect';
    });

  });

</script>

此时:PayBack Action如下:

 [HttpGet]
    public async Task<IActionResult> PayBack()
    {
      var code = Request.Query["code"];
      var state = Request.Query["state"];
      OAuthToken tokenModel = new OAuthToken();
      //通过code换取token
      if (!string.IsNullOrEmpty(code))
      {
        _logger.LogWarning("授权成功");
        ViewBag.Code = code;
        tokenModel = OauthApi.GetAuthToken(code, wechatAppId);
      }

      var request = new WeChatPayUnifiedOrderRequest
      {
        Body = "微信公众号支付测试",
        OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
        TotalFee = 1,//分 单位
        SpbillCreateIp = "127.0.0.1",
        NotifyUrl = "http://pay.one.com/notify/wechatpay/unifiedorder",
        TradeType = "JSAPI",
        OpenId = tokenModel.Openid //此处需进行授权 获取OpenId
      };
      var response = await _client.ExecuteAsync(request);
      _logger.LogWarning($"统一下单接口返回:{response.ReturnCode}");

      if (response.ReturnCode == "SUCCESS" && response.ResultCode == "SUCCESS")
      {
        var req = new WeChatPayH5CallPaymentRequest
        {
          Package = "prepay_id=" + response.PrepayId
        };
        var parameter = await _client.ExecuteAsync(req);
        // 将参数(parameter)给 公众号前端 让他在微信内H5调起支付(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6)
        ViewData["parameter"] = JsonConvert.SerializeObject(parameter);
        _logger.LogWarning($"统一下单成功,即将调起微信支付:{ViewData["parameter"].ToString()}");
        ViewData["response"] = response.Body;
        return View();
      }
      ViewData["response"] = response.Body;

      return View();
    }

其中:OAuthToken是网页授权 返回的实体:

/// 获取网页授权token时,返回的实体
  /// </summary>
  public class OAuthToken : BaseRes
  {
    /// <summary>
    /// 网页授权接口调用凭证。注意:此access_token与基础支持的access_token不同
    /// </summary>
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
    private int _expiresIn;
    /// <summary>
    /// access_token接口调用凭证超时时间,单位(秒)
    /// </summary>
    [JsonProperty("expires_in")]
    public int ExpiresIn
    {
      get { return _expiresIn; }
      set
      {
        ExpiresTime = DateTime.Now.AddSeconds(value);
        _expiresIn = value;
      }
    }
    /// <summary>
    /// 用于刷新access_token
    /// </summary>
    [JsonProperty("refresh_token")]
    public string RefreshToken { get; set; }
    /// <summary>
    /// 用户唯一标识。请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的openid
    /// </summary>
    [JsonProperty("openid")]
    public string Openid { get; set; }
    /// <summary>
    /// 用户授权的作用域,使用逗号(,)分隔
    /// </summary>
    [JsonProperty("scope")]
    public string Scope { get; set; }
    [JsonProperty("expires_time")]
    public DateTime ExpiresTime { get; set; }
    /// <summary>
    /// 只有在用户将公众号绑定到微信开放平台账号后,才会出现该字段
    /// </summary>
    [JsonProperty("unionid")]
    public string Unionid { get; set; }
  }

最后 贴一下支付成功后的回调函数:

[Route("notify/wechatpay")]
  public class WeChatPayNotifyController : Controller
  {
    private readonly IWeChatPayNotifyClient _client;
    private readonly ILogger<WeChatPayNotifyController> _logger;
    public WeChatPayNotifyController(IWeChatPayNotifyClient client,ILogger<WeChatPayNotifyController> logger)
    {
      _client = client;
      _logger = logger;
    }

    /// <summary>
    /// 统一下单支付结果通知
    /// </summary>
    /// <returns></returns>
    [Route("unifiedorder")]
    [HttpPost]
    public async Task<IActionResult> Unifiedorder()
    {
      try
      {
        _logger.LogWarning($"进入回调");
        var payconfig = OpenApi.GetPayConfig();
        var notify = await _client.ExecuteAsync<WeChatPayUnifiedOrderNotify>(Request);
        _logger.LogWarning($"返回状态码:{notify.ReturnCode}");

        if (notify.ReturnCode == "SUCCESS")
        {
          _logger.LogWarning($"业务结果码:{notify.ResultCode}");

          if (notify.ResultCode == "SUCCESS")
          {
            _logger.LogWarning($"支付方式:{notify.TradeType}");
            _logger.LogWarning($"商户订单号:{notify.OutTradeNo}");
            _logger.LogWarning($"微信支付订单号:{notify.TransactionId}");
            _logger.LogWarning($"支付金额:{notify.TotalFee}");
            return WeChatPayNotifyResult.Success;
          }
        }
        return NoContent();
      }
      catch(Exception ex)
      {
        _logger.LogWarning($"回调失败:{ex.Message}");
        return NoContent();
      }
    }
}

然后测试一下支付,查看服务器Log如下:

H5支付

H5支付是指再除开微信浏览器以外的移动端浏览器上进行微信回复操作。

和上面步骤大体一致,有几个地方需要注意

1:客户端IP问题:H5支付的时候,微信支付系统会根据客户端调起的当前Ip 作为支付Ip,若发现 发起支付请求时,ip有问题,则会支付失败,或者提示系统繁忙。这里贴一下我获取IP的代码:

Utils.GetUserIp(_accessor.HttpContext);//页面上调用

    /// <summary>
    /// 穿过代理服务器获取真实IP
    /// </summary>
    /// <returns></returns>
    public static string GetUserIp(this HttpContext context)
    {
      var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
      if (string.IsNullOrEmpty(ip))
      {
        ip = context.Connection.RemoteIpAddress.ToString();
      }
      return ip;

    }

2:TradeType类型应该是:MWEB

3:若调起微信支付成功后,默认回调到支付首页,若需要设置回调页面,则可以再URl中拼接:

  /// <summary>
    /// H5支付
    /// </summary>
    /// <param name="viewModel"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<IActionResult> H5Pay(WeChatPayH5PayViewModel viewModel)
    {
      var request = new WeChatPayUnifiedOrderRequest
      {
        Body = viewModel.Body,
        OutTradeNo = viewModel.OutTradeNo,
        TotalFee = viewModel.TotalFee,
        SpbillCreateIp = viewModel.SpbillCreateIp,
        NotifyUrl = viewModel.NotifyUrl,
        TradeType = viewModel.TradeType
      };
      var response = await _client.ExecuteAsync(request);

      // mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
      if (response.MwebUrl == null)
      {
        ViewData["response"] = response.ReturnMsg;
        return View();
      }
      return Redirect(response.MwebUrl);
    }

更多详细可参考文档: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4

4:支付结果通知:

注意:

1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起10次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【 查询订单API】确认订单状态。

特别提醒:

1、商户系统对于支付结果通知的内容一定要做 签名验证,并校验返回的订单金额是否与商户侧的订单金额一致 ,防止数据泄漏导致出现“假通知”,造成资金损失。

2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

最后可以测试下H5支付,查看返回的Log:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ASP.NET Core 2.0 使用支付宝PC网站支付实现代码

    前言 最近在使用ASP.NET Core来进行开发,刚好有个接入支付宝支付的需求,百度了一下没找到相关的资料,看了官方的SDK以及Demo都还是.NET Framework的,所以就先根据官方SDK的源码,用.NET Standard 2.0 实现了支付宝服务端SDK,Alipay.AopSdk.Core(github:https://github.com/stulzq/Alipay.AopSdk.Core) ,支持.NET CORE 2.0.为了使用方便,已上传至Nuget可以直接使用. 支付

  • ASP.NET Core 2.0 支付宝扫码支付的实现代码

    前言 自从微软更换了CEO以后,微软的战略方向有了相当大的变化,不再是那么封闭,开源了许多东西,拥抱开源社区,.NET实现跨平台,收购xamarin并免费提供给开发者等等.我本人是很喜欢.net的,并希望.net core能够崛起.我是从.net core 1.1的时候开始使用的,到现在的.net core 2.0..net core 2.0比1.1有了一些改变,api也增加了很多,用着更顺手了,最近在做asp.net core 对接支付宝,百度了一下没找到关于core的支付宝支付相关资料,所以

  • python如何导出微信公众号文章方法详解

    1.安装wkhtmltopdf 下载地址:https://wkhtmltopdf.org/downloads.html 我测试用的是windows的,下载安装后结果如下 2 编写python 代码导出微信公众号文章 不能直接使用wkhtmltopdf 导出微信公众号文章,导出的文章会缺失图片,所以需要使用 wechatsogou 将微信公众号文章页面抓取,之后将html文本转化为pdf pip install wechatsogou --upgrade pip install pdfkit 踩坑

  • 微信公众号H5支付接口调用方法

    本文实例为大家分享了 微信内H5调用支付接口的具体代码,供大家参考,具体内容如下 官方文档地址 <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>微信公众号H5接口调用</title> <script src='./js/md5.js'></script> </head>

  • php微信公众号开发模式详解

    学习步骤:分四章来讲述这部分内容,下面是每章的大致内容. 1.了解开发模式与编辑模式,开发前的一些准备. 2.开发模式用户.微信服务器.个人服务器是如何交互的.什么是接口. 3.各种接口功能的调用与实现. 4.js-SDK的调用 微信公众号开发两种模式:编辑模式和开发模式.编辑模式比较简单,你不需要操作任何的代码,只需要借助微信提供的功能来管理自己的微信公众号.这种方式开发的页面比较简单,主要用来实现文章的推送等功能.开发者模式则能通过自己的后台服务器与微信关注用户实现更多的交互作用,调用微信的

  • Python selenium爬取微信公众号文章代码详解

    参照资料:selenium webdriver添加cookie: https://www.jb51.net/article/193102.html 需求: 想阅读微信公众号历史文章,但是每次找回看得地方不方便. 思路: 1.使用selenium打开微信公众号历史文章,并滚动刷新到最底部,获取到所有历史文章urls. 2.对urls进行遍历访问,并进行下载到本地. 实现 1.打开微信客户端,点击某个微信公众号->进入公众号->打开历史文章链接(使用浏览器打开),并通过开发者工具获取到cookie

  • 微信小程序应用号开发教程详解

    微信应用号(微信公众平台小程序,「应用号」的新称呼)终于来了!开源中国社区的博卡君通宵吐血赶稿写出的微信公众平台应用号开发教程!大家赶紧来学习一下吧 微信公众平台小程序目前还处于内测阶段,微信只邀请了部分企业参与封测.想必大家都关心应用号的最终形态到底是什么样子?怎样将一个「服务号」改造成为「小程序」? 我们暂时以一款简单的第三方工具的实例,来演示一下开发过程吧.(公司的项目保密还不能分享代码和截图.博卡君是边加班边偷偷给大家写教程.感谢「名片盒」团队提供他们的服务号来动这个手术,所以博卡君的教

  • .NET Core之微信支付之公众号、H5支付详解

    前言 本篇主要记录微信支付中公众号及H5支付全过程. 准备篇 公众号或者服务号(并开通微信支付功能).商户平台中开通JSAPI支付.H5支付. 配置篇 公众号或者服务号中 -------开发-------开发者工具---------web开发者工具-------绑定为开发者 公众号或者服务号中 -------公众号设置--------功能设置   :填写业务域名.JS安全域名.网页授权域名 示例:pay.one.com 商户平台中--------产品中心-------开发配置------JSAP

  • 微信支付之公众号支付(java实现)

    最近两周实现了调用微信接口使用微信进行支付的需求,包含公众号支付及扫码支付两种方式,由于微信文档写的较为简略,现将调用微信接口进行支付流程进行记录及分享. 本文旨在对公众号支付的实现流程进行介绍,即微信用户从公众号中点击链接进入商品h5页面,选择商品后点击支付按钮后弹出微信支付页面.输入支付密码.支付成功后跳转到全部商品页面的整个过程.微信扫码支付请参看后续文章. 1.首先,商户需申请微信公众号.微信商户号及微信支付权限.开发过程中需参照公众号及商户平台提供如下参数: ① appid:公众号id

  • php微信扫码支付 php公众号支付

    本文实例为大家分享了php微信扫码支付,公众号支付的具体代码,供大家参考,具体内容如下 <?php # 微信统一下单接口 $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $param = [ 'appid' => '公众号id', 'mch_id' => '商户id', 'nonce_str' =>uniqid(), 'sign_type' => 'MD5', 'body' => 'test', 'det

  • SpringBoot + 微信公众号JSAPI支付功能的实现

    1.pom.xml依赖配置 <!-- 微信支付 --> <dependency> <groupId>com.egzosn</groupId> <artifactId>pay-java-wx</artifactId> <version>2.12.4</version> </dependency> 2.application.yml文件配置微信公众号的基础信息 #微信公众号支付配置 wechatpay:

  • php实现银联商务公众号+服务窗支付的示例代码

    之前我们学习了银联商务的H5支付,但是现在H5支付不支持微信支付,最后发现银联商务的公众号+服务窗支付可以支持支付宝支付+微信支付+银联支付 一:接口地址: 测试接口地址:https://qr-test2.chinaums.com/netpay-portal/qmf/webPay.do? 正式接口地址:https://qr.chinaums.com/netpay-portal/qmf/webPay.do? 二:主要参数说明: 接口使用的是get传参,直接将接口参数放到接口地址后,此接口是由浏览器

随机推荐