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

1|1准备篇

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

1|2配置篇

公众号或者服务号中 -------开发-------开发者工具---------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

1|3开发篇

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是网页授权 返回的实体:

 

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

 
[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:

本文只是抛砖引玉,更多具体支付场景和代码,还需各位看官结合自己项目量身定做。

更多示例Demo可入群获取。

__EOF__

作  者:潇十一郎
出  处:https://www.cnblogs.com/zhangxiaoyong/p/10588241.html

公众号及H5支付的更多相关文章

  1. .NET Core之微信支付之公众号、H5支付篇

    前言 本篇主要记录微信支付中公众号及H5支付全过程. 准备篇 公众号或者服务号(并开通微信支付功能).商户平台中开通JSAPI支付.H5支付. 配置篇 公众号或者服务号中 -------开发----- ...

  2. 集成微信支付的代码。兼容小程序,扫码,app,公众号。h5 支付 ,以及 服务商提现

      /** * 因为微信总是很多个商户号很多和appid.很多个密钥文件,所以全部改成手动传值的方式,就可以支持多商户调用 * * @param appId 商户的appid * @param mch ...

  3. 微信公众号内H5调用微信支付国内服务商模式

    最近在折微信公众号内H5用JSAPI调用微信支付,境内服务商版支付,微信支付给出的官方文档以及SDK不够详细,导至我们走了一些弯路,把他分享出来,我这边主要是用PHP开发,所以未加说的话示例都是PHP ...

  4. [5] 微信公众号开发 - 微信支付功能开发(网页JSAPI调用)

    1.微信支付的流程 如下三张手机截图,我们在微信网页端看到的支付,表面上看到的是 "点击支付按钮 - 弹出支付框 - 支付成功后出现提示页面",实际上的核心处理过程是: 点击支付按 ...

  5. PHP微信公众号JSAPI网页支付(上)

    一.使用场景以及说明 使用场景:商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程. 说明:1.用户打开图文消息或者扫描二维码,在微信内置浏览器打开网 ...

  6. 免费微信公众号专用h5在线电影票API

    免费h5在线电影票API,通过嵌套返回的h5页面url,实现电影票购买. 接口文档:https://www.juhe.cn/docs/api/id/252,通过此申请APPKEY 接口备注:通过请求返 ...

  7. 微信公众号、H5、APP三者各有什么优势?

    昨天给大家分享了一个现在很热的H5,众所周知,当下H5手机网站.微信公众号.APP这三种载体都越来越火了,而且三者都有各自的一些优势和劣势. HTML5(H5) H5之所以能引发如此广泛的效应,根本在 ...

  8. PHP微信公众号JSAPI网页支付(下)

    上一篇PHP微信公众号JSAPI网页支付(上)中讲到了公众号平台的相关设置以及支付的大致流程. 这一篇重点讲支付后,异步接受回调通知,以及处理后同步通知微信服务器. 首先梳理下整个jsapi支付的流程 ...

  9. [转]微信小程序、微信公众号、H5之间相互跳转

    本文转自:https://www.cnblogs.com/colorful-paopao1/p/8608609.html 转自慕课网 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加 ...

随机推荐

  1. Java Volatile关键字 以及long,double在多线程中的应用

    概念: volatile关键字,官方解释:volatile可以保证可见性.顺序性.一致性. 可见性:volatile修饰的对象在加载时会告知JVM,对象在CPU的缓存上对多个线程是同时可见的. 顺序性 ...

  2. Unity3D获取系统当前时间,并格式化显示

    Unity 获取系统当前时间,并格式化显示.通过“System.DateTime”获取系统当前的时间,然后通过格式化把获得的时间格式化显示出来,具体如下: 1.打开Unity,新建一个空工程,Unit ...

  3. springboot2.0 最大上传文件大小遇到的错误Failed to bind properties under 'spring.servlet.multipart.max-file-size'

    错误: 解决: 把100Mb改为100MB

  4. [原][译]JSBSim官方源码文档翻译(google翻译)

    /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS DOCUMENTATION ...

  5. Uncaught DOMException: Failed to construct 'WebSocket': The URL 'xxx.xxx.com/' is invalid.

    Uncaught DOMException: Failed to construct 'WebSocket': The URL 'xxx.xxx.com/' is invalid. 出现这个问题是构造 ...

  6. Hive常用操作命令

    创建数据库>create database db_name;>create database if not exists db_name;//创建一个不存在的数据库final查看数据库&g ...

  7. 清理SuperMap三维缓存

    一.iclient三维插件浏览器端缓存 方法1:遍历users子文件夹,发现指定目录有supermap,即删除 @echo offtitle 清理三维缓存@echo 开始清理三维缓存...for /f ...

  8. flask项目部署

    1.安装 安装ssh 新版本安装位 apt/apt-get 老版本为 apt-get 安装以后 ifconfig查看当前的ip地址 如果网络不在同一个ip段 将网络模式设置为桥接 安装pip3 sud ...

  9. 关于vs调用数据库存储过程 返回输出参数的一些总结

    1.直接上练习的存储过程,方便回想 create proc proc_output @totlecount int output, @pageIndex int, @pageSize intas de ...

  10. Spring Developer Tools 源码分析:二、类路径监控

    在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...