0x0001 前言
经过一周的摧残,终于完成了微信支付,做完总结一下,觉得其实并不难,主要是网上没有一个明确的教程.
本文是基于C#语言上进行开发,其实本人是做java的,但由于最近接手维护的项目是C#的,所以也强制让自己简单的熟悉一遍C#(偷偷的说,我C# .net .asp都还没清楚,不过最重要的是思路,语言只是一种工具~~~)
0x0002 项目背景(可略过)
- 项目类型:在线考试系统;
- 项目要求:让之前全部免费的试卷部分收费,故选用微信支付 (PS:早知道微信支付官方文档这么坑,就选型支付宝了)
- 项目业务:系统暂时要求点击支付时网站弹出生成的微信支付二维码,由用户进行扫描购买,购买完成后用户可使用已购买的商品(试卷);
- 微信支付产品选型:微信有多种支付模式,如下:
根据实际情况需求,推荐使用Native支付,此种方式简单也能完成我们的需求.(一开始选的JSAPI,但是后来实在做不动了,就换了Native支付,不过现在看来,都差不多,只是JSAPI需要多获取几个微信指定的属性)
Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站、实体店单品或订单、媒体广告支付等场景。
0x0003 开发思路
- 简单来说:微信扫码支付就是我们把需要的信息生成二维码,然后用户扫描二维码就出现了我们想要展示的信息.
- 稍复杂来说:微信扫码支付就是我们把部分重要信息(商品价格\商品id等)发送给微信服务端,微信返回生成包含我们提交数据且加密的微信支付协议的URL,我们接收此URL并将其生成一个二维码图片,用户通过微信扫一扫,发起支付。
- 总体来说:
1.注册微信商户号,接入微信支付,配置微信支付平台的 支付授权目录 和 扫码回调链接;
2.用户点击支付时,前端向服务端传递相关数据id;
3.服务端接收id后查询核对,按照微信扫码支付规则调用微信支付接口;
4.微信支付接口根据一系列数据比如(商品价格\商品id)加密生成签名后返回一个带微信支付协议的URL;
5.使用相关生成二维码的工具(QRCode.js)将URL生成二维码图片;
6.用户扫码跳到支付界面,点击支付后,微信服务端调用我们的扫码回调链接;
7.需要提前写好扫码回调链接的类或方法,根据微信传来的诗句在此核对数据,通过则更改自己系统的某状态,并返给微信服务端成功.
0x0004 开发过程
0.准备工作
- 申请 微信商户号 微信公众号 (这里具体步骤不描述,因为这属于公司申请):
- 开通 Native支付(点击开通,很简单,但需要管理员账号)
- 安装 微信认证证书
1.下载官方工具类(Native 模式二)
官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
先下载官方提供的工具类Demo
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
2.解压并进入WxPayAPI文件夹,将 business example lib 三个文件夹复制到咱们项目的工程中(本项目使用VS ,如果不会添加 ,VS将复制过来的文件或文件夹显示到解决方案管理)
3.虽然是三个文件夹,但对于我们Native开发模式二,仅需改3个文件,也就是每个文件夹下一个,就可以完美运行]
(1)business文件夹 NativePay.cs 推荐其他文件不删除
(2)example文件夹 ResultNotifyPage.aspx,推荐删除其他文件,并将此文件放在工程根目录下
如:
(3)lib文件夹全部保留,因为这里面是所依赖的工具类。
4.后端进行页面配置
(1)对于lib文件夹,仅需修改DemoConfig.cs[作用:配置微信商户号和公众号、回调地址等相关信息];
对于这个页面,我们仅需要修改4个地方:
将内容写在 return 后面的引号里
以下是这4个的介绍
①Appid:绑定支付的APPID(必须配置) 登录微信公众号可以查到 (商户号必须绑定一个公众号才可以,这个APPID是微信公众号的);
②Mchid:商户号(必须配置) 登录 微信商户平台 可以查到 ;
③ Key:商户支付密钥,参考开户邮件设置(必须配置),请妥善保管,避免密钥泄露 ;
④NotifyUrl: 支付结果通知回调url,用于商户接收支付结果
注:a.这个链接必须外网,如需内网开发测试可参考微信支付-使用Natapp实现本地内网进行开发测试以及解决webpack "Invalid Host header"问题
\b.ResultNotifyPage.aspx为刚才 **3(2)**中所保留的那个文件
(2)对于business文件夹,仅需修改NativePay.cs文件[作用:用来调用微信接口根据我们提供的数据生成二维码链接]
下拉代码找到模式二,仅需修改模式二的代码
①可修改方法参数列表 product_id不要动,其他的可随意修改,传递值时,将主键传到product_id中
②可修改data.SetValue(键,值)的值
键可参考 官方文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
我这里只给出上面图片显示的键对应的内容:
注1:body商品描述 是用户在扫码后显示的标题,如下
微信支付-C# .net 微信扫码支付Body含有中文导致出现“签名错误”
注2:total_fee商品金额 单位是分!!!单位是分!!!单位是分!!!推荐传值或在当前页面直接根据商品id直接查金额,防止被篡改;
注3:trade_type交易类型 一定为NATIVE. product_id商品ID不要改,接收传递的ID即可;
注4:其他属性可保持原来的或自己添加 .
(2)对于ResultNotifyPage.aspx文件,仅需修改ResultNotifyPage.aspx.cs文件[作用:用户支付成功后,微信服务端发送信息到这个页面提醒我们核对信息并更新状态]
原版代码为:
我们要获取微信服务端返回来的数据使用上述代码的这个方法 resultNotify.ProcessNotify();
此方法有返回值,原版代码没写,在这里我们需要写上返回值.
WxPayData notifyData = resultNotify.ProcessNotify();
然后开始写逻辑:关于我系统的代码如下:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using TeachAssis.Model;
namespace WxPayAPI
{
public partial class ResultNotifyPage : System.Web.UI.Page
{
private TeachAssis.Service.OrderService orderService = new TeachAssis.Service.OrderService();//项目所用的业务逻辑层
private TeachAssis.Service.StuExamService stuExamService = new TeachAssis.Service.StuExamService();//项目所用的业务逻辑层
protected void Page_Load(object sender, EventArgs e)
{
ResultNotify resultNotify = new ResultNotify(this);
WxPayData notifyData = resultNotify.ProcessNotify();//添加上返回值;
if (notifyData != null)//判断微信传递的数据是否为空,不为空则可以进入
{
//支付回调,微信连续发送8次请求,result_code此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断,可参考 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
if (notifyData.IsSet("result_code"))//成功返回 SUCCESS 则进入判断
{
//1.通过传递的订单号先查询是否存在此订单,如果订单存在则进行下一步 这一步已经在 resultNotify.ProcessNotify()方法中自行完成
//2.核对系统订单金额与微信订单中金额是否一致
//2.1 先通过传递的考试订单id从订单表查询订单金额
String order_id=(String)notifyData.GetValue("attach");
Orders ord = orderService.GetModel(order_id);
if (ord!=null) {
//2.2 将查出的金额单位由元变为分
int money = (int)(ord.ExamMoney * 100);
//2.3 与微信订单中金额进行对比,不一致,返回非法操作
int WXMoney = Convert.ToInt32(notifyData.GetValue("total_fee"));
if (money == WXMoney)
{
//签名
String sign = ((String)notifyData.GetValue("sign")).ToUpper();
if (sign!=null&&!"".Equals(sign))
{
ord.Test3 = sign;
//3.核对其他信息是否正确
String sign2 = notifyData.MakeSign().ToUpper();
if (sign.Equals(sign2))
{
//将结束时间存到当前订单中
String time_end = (String)notifyData.GetValue("time_end");
ord.EndTime = time_end;
//将微信给商户生成的订单号存在当前订单中Test1
String out_trade_no = (String)notifyData.GetValue("out_trade_no");
ord.Test1 = out_trade_no;
//将微信订单号存在当前订单中PayID
String transaction_id = (String)notifyData.GetValue("transaction_id");
ord.PayID = transaction_id;
//将微信用户用户标识(用户在商户appid下的唯一标识)存在当前订单中Test2
String openid = (String)notifyData.GetValue("openid");
ord.Test2 = openid;
//将是否关注公众账号存在当前订单中Test3(用户是否关注公众账号,Y-关注,N-未关注)
String is_subscribe = (String)notifyData.GetValue("is_subscribe");
ord.Test3 = is_subscribe;
//在Orders表中修改订单的状态为2,已支付
ord.PayStatus = 2;
b = orderService.Save(ord);
//在StuExam表中新建一条记录
StuExam stuExam = new StuExam { StuID = ord.StuID, CreatTime = DateTime.Now, ExamID = ord.ExamID, Status = 1 };
if (stuExamService.IsExsit(ord.StuID, ord.ExamID)) {
//如果存在记录,则只需要更改状态
String[] strPK = { ord.StuID, ord.ExamID };
StuExam stu = stuExamService.GetModel(strPK);
stu.Status = 1;
stuExamService.Save(stu);
}
else {
//如果不存在记录
stuExamService.Add(stuExam);
}
}
}
}
else
{
//暂无操作
}
}
}
}
}
}
}
其实代码很简单,就是将 notifyData里的数据,通过 notifyData.GetValue("XXXX")
方法获取出来,然后和订单中比一下,再调用微信生成签名的工具重新生成签名对比一下,保证安全性,如果通过,则修改订单状态即可.
到这里所有后台需要修改的微信代码全部完成,还缺一个接收通过微信接口生成的微信支付协议的链接,通过这个链接我们在前台转换成二维码即可.
5.前端进行页面配置
本系统前端使用vue,生成二维码的工具为QRCode.js
QRCode在一般项目中引入可参考 http://www.runoob.com/w3cnote/javascript-qrcodejs-library.html
在vue中
0.在项目文件夹中的打开cmd命令窗口输入npm install qrcodejs2 --save
安装qrcode
1.在使用的界面导入 import QRCode from 'qrcodejs2'
使用qrcode
2.在使用的位置放入div <div id="qrcode"></div> //生成二维码的位置
3.在js中(vue项目中) methods:{} 中写以下方法,先获取元素节点,然后将节点内容清空,使用new QRCode(qr,Xxx);
Xxx是微信接口返给我们的URL地址
4.调用此方法:我的调用流程为,点击报名(在点击事件中将想要购买的商品id通过路由传到微信支付接口)
,然后弹出模态窗口(微信支付接口生成url链接,QRCode根据url生成图片展示在页面上),
附部分代码和逻辑:(这里其实可以很简单的写,看自己的习惯)
这里使用了延时加载,因为有时候页面刷新太快,导致加载跟不上.
到这里就写完了,虽然内容很多,总结一下很少
1.下载微信demo,将那三个文件夹加入工程
2.修改那3个文件
3.前端向微信接口传值,返回URL
4.QRCode根据URL生成二维码