开发前,我们先去官网下载一个Demo(根据自己的开发语言选择不同的版本):
https://pay.weixin.qq.com/wiki/doc/api/native_sl.php?chapter=11_1
第一步,打开这个Demo,首先我们看这个获取支付二维码地址的方法:
/**
* 生成直接支付url,支付url有效期为2小时,模式二
* @param productId 商品ID
* @return 模式二URL
*/
public string GetPayUrl(string productId)
{
WxPayData data = new WxPayData();
data.SetValue("body", "扫码支付--模式二");//商品描述
data.SetValue("attach", "可以添加需要传递的数据");//附加数据
data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
data.SetValue("total_fee", 1);//总金额
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
data.SetValue("goods_tag", "jjj");//商品标记
data.SetValue("trade_type", "NATIVE");//交易类型
data.SetValue("product_id", productId);//商品ID
WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
return url;
}
这里面会设置一些参数,需要注意的地方,我觉得有三个点:
1. 设置“attach”。
这个东西注释解释的是附加数据,其实你可以理解为一个扩张字段,用于存放你下单后需要取的数据。举一个具体的例子:我有十个售货机,编号1-10,假如我从第一台售货机买了一个东西,然后在售货机生成支付二维码的时候,我在attach中存放一个数据:5,然后等待支付完成后,我在后台获得这笔支付的订单的时候,根据订单中attach的值,我就能知道是哪台机器的订单了。(这只是个举例,可以这么用,但是他的用处肯定不会这么小啦,各位理解就好。。)
2.设置“total_fee”。
我们看到这个东西设置的是1,但是其实支付的时候显示的是0.01元,这是因为微信的最小单位是1元。我的意思就是,,如果你要设置扫码支付的价格是1元的话,那么这里你就要写100。
3.设置“trade_type”。
这个是代表的支付类型,如果你用的是扫码支付,那么这里就是写“NATIVE”。当然还有其他的支付方式,比如:调用微信的支付,那么这里就需要写“JSAPI”啦。
第二步,设置完这些就准备下单了(UnifiedOrder)
/**
*
* 统一下单
* @param WxPaydata inputObj 提交给统一下单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
*/
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//检测必填参数
if (!inputObj.IsSet("out_trade_no"))
{
throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
}
else if (!inputObj.IsSet("body"))
{
throw new WxPayException("缺少统一支付接口必填参数body!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new WxPayException("缺少统一支付接口必填参数total_fee!");
}
else if (!inputObj.IsSet("trade_type"))
{
throw new WxPayException("缺少统一支付接口必填参数trade_type!");
}
//关联参数
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
{
throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
}
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
{
throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
}
//异步通知url未设置,则使用配置文件中的url
if (!inputObj.IsSet("notify_url"))
{
inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
}
inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
//签名
inputObj.SetValue("sign", inputObj.MakeSign());
string xml = inputObj.ToXml();
var start = DateTime.Now;
string response = HttpService.Post(xml, url, false, timeOut);
var end = DateTime.Now;
int timeCost = (int)((end - start).TotalMilliseconds);
WxPayData result = new WxPayData();
result.FromXml(response);
ReportCostTime(url, timeCost, result);//测速上报
return result;
}
在下单里面需要注意的几个方面:
1.异步通知url未设置。
这个URL的用处就是,你支付完成之后的回调函数,作用是用来告诉你支付完成啦(同时,微信会把本次支付的订单的详情也返回到这个URL地址的界面)。
2.特别特别要注意的问题来了。
首先看下这段代码:
inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
这里面就牵扯到另一个概念了:微信的服务商和商户。
一般的情况是这样子,一个微信公众号对应一个微信服务商,一个微信服务商下面可以有多个商户。这句话什么意思呢?举个例子(感觉有不准的地方,大神们请斧正下):一个微信公众号相当于一个上市公司,这个微信服务商相当于这个上市公司的财务部门,管收钱的,就是你们下来(微信支付,扫码支付等)的钱和出去(结算,转账)的钱,我都可以打到这个账户上。
那上面的appid就相当于公司的代码一样,mch_id就相当于财务的银行账号(我不习惯管这个叫商户号,因为我觉得更像是服务商号),我需要打钱,直接填写上面的信息就可以了。
但是有很多公司是有独立子公司的(也就是独立营收的,账务独立),意思就是,我子公司的钱我是不会直接打到总公司账上去的,,这个时候我需要他打钱到我子公司的账户上,所以我还需要把我子公司的账号给他,就变成如下代码了:
inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
inputObj.SetValue("sub_mch_id", WxPayConfig.SUB_MCH_ID);
发现多了一个字段:sub_mch_id。这个就是我子公司的账号,对应微信里面这个就是真正的商户的商户号。
第三步,设置完这些,我们直接调用GetPayUrl方法后就会获得微信返回的一个二维码的地址,我们拿到这个地址,只需要把他转为图片,显示到页面上,等待扫码支付然后就完成了。
第四步,异步通知url。
第二步的时候说到一个异步通知URL的问题,其实就是一个接受微信支付结果的地址。
首先我们找到微信官方Demo这个文件下面的代码看下:
namespace WxPayAPI
{
public partial class ResultNotifyPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ResultNotify resultNotify = new ResultNotify(this);
resultNotify.ProcessNotify();
}
}
}
很简单的几行代码,关键点在于resultNotify.ProcessNotify()这一行,返回的数据解析如下(以下数据已做修改,不是真实数据,原因你懂的。。。)
大体的返回信息如上,你只要看到result_code=SUCCES,那么恭喜你,
你的整个流程已经成功99%啦。等等,为什么说成功99%,而不是100%,这就要说另一个坑啦,都怪自己当时没仔细阅读每一行文档。
这么说吧,我们做程序的一般都是等待支付完成后还有其他的操作,,也就是真实逻辑并没有走完。比如用户支付成功了,我们会给他推送消息,APP消息,微信模板消息等等的消息或者其他操作,比如积分变动啊,这时候仅仅依靠result_code=SUCCES去判断会有一个问题,当时楼主后续操作就是会发送微信模板消息,然后靠这个判断,造成的结果就是支付一次成功后会不断的给我发消息,最后还是看了文档中提到,微信的支付回调函数是会多次回调的,也就是多次访问你设置的那个异步通知URL,然后每访问一次就会发一次。
所以还需要我们人为的判断,比如,我会在订单建立之初创建一条记录,记录的支付状态字段为:未支付。下单的时候把这个记录的ID放到订单的attach字段中,等到在回调函数中收到已完成的订单,,取出attach内容(记录ID),然后根据这个ID去查我数据库中这条记录的支付状态,如果是未支付,那么改为已支付,同时发送微信模板消息。如果是其他状态,比如:已支付或者取消支付等,我都不做操作,直接返回false。
好啦,到这里基本整个流程就走完啦。