最近在做一个涉及支付的项目,其中对支付模块的内容涉及的比较多,记录一点心得。如果有错误,希望各位大佬斧正。
这个支付项目涉及H5端的微信支付、支付宝支付、建行支付、会员卡充值(现金)等四部分内容。我将在下面的几篇文章里分别介绍。
这篇文章我们先介绍微信支付。
微信支付现在已经是V3版本了,和之前的V2版本有很大的区别。我之前也做过V2版本的小程序支付。涉及到一些xml、签名等比较麻烦。V3版本感觉简化了很多。
关于测试环境
微信支付和支付宝相比,文档和测试环境都很差,排查错误很难。至于V3版本,我去微信支付的社区提问,得到的答复是根本就没有沙箱环境。这就超坑。
sdk和git上有一个postman的脚本,但是也仅仅是下载证书的,没有调测支付接口的。链接https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
总之就是没有测试环境。你只能先弄H5的支付申请,然后开通之后再进行开发。
关于开通H5支付
开通H5支付必须需要一个备案过的顶级域名(这东西要钱),可以在阿里云上买一个,备案一下,有过建站经验的小伙伴应该比较熟悉,我不太懂,让客户自己买的。
至于实际使用的是顶级域名下面的二级域名这个是无所谓的。例如备案的是www.baidu.com,实际部署和使用的是www.test.baidu.com。这都可以
还需要一个H5商品的介绍页。这里我是做了一个产品介绍页,并没有实际售卖的产品,类似于奔驰官网那种布局,上面导航栏,下面产品介绍(不知道啥时候能码出奔驰的钱)。
然后部署在二级域名下面,就可以了。
这里要着重说一下,你的域名下的官网或产品介绍页或商城必须能够打开,如果打不开就被申请驳回。
申请需要3-5个工作日,但是也不一定,我的一个工作日就通过了
下面就是微信商户平台的申请页面了
支付参数
开通之后你是不是准备码代码了,别着急,你还需要配置一下各种参数。
微信给了文档教程:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_1.shtml
首先你需要申请一下证书,参考上面的文档就好了。
你需要以下参数
商户号
appid:如果没有需要申请一个公众号或者服务号,然后在商户平台绑定这个appid
证书序列号:申请API证书之后点API证书那一栏的查看证书,就能在弹出页里面看到序列号了
apiV3Key:需要你在API安全里设置
这样就ok了,准备写代码
支付下单
写代码之前你需要查阅文档
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_2.shtml
文档里下单发起支付有两段代码,拼在一起就行了
这一段里面涉及一个privateKey,比较坑,你需要自己用代码把证书里面的privateKey解出来。或者从文件里读取出来
代码如下
ClassPathResource resource = new ClassPathResource(WxPayConfig.mchSerialFileName);
File file=resource.getFile();
byte[] readBytes = FileUtil.readBytes(file);
这个mchSerialFileName 就是你申请的证书文件的.pem文件的路径
@Before
public void setup() throws IOException {// 加载商户私钥(privateKey:私钥字符串)PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3秘钥)AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));// 初始化httpClienthttpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();
}@After
public void after() throws IOException {httpClient.close();
}
public void CreateOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/h5");// 请求body参数
String reqdata = "{"+ "\"amount\": {"+ "\"total\": 100,"+ "\"currency\": \"CNY\""+ "},"+ "\"scene_info\": {"+ "\"payer_client_ip\":\"14.23.150.211\","+ "\"h5_info\": {"+ "\"type\": \"IOS\"" + "}},"+ "\"mchid\": \"1900006891\","+ "\"description\": \"Image形象店-深圳腾大-QQ公仔\","+ "\"notify_url\": \"https://www.weixin.qq.com/wxpay/pay.php\","+ "\"out_trade_no\": \"1217752501201407033233388881\","+ "\"goods_tag\": \"WXG\","+ "\"appid\": \"wxdace645e0bc2c424\"" + "}";
StringEntity entity = new StringEntity(reqdata);
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));} else if (statusCode == 204) {System.out.println("success");} else {System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));throw new IOException("request failed");}
} finally {response.close();
}
}
这样你就下单成功了。
接口会返回一个
{ "h5_url": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241" }
的json,这个就是发起请求的链接了。
但是这个链接不能通过浏览器直接打开,会报下面的错误
所以你需要在app或者H5里面使用代码打开。
例如:先用ajax请求后台,后台下单返回链接,然后打开。至于网上说的通过response.sendRedirect的方法,反正我是没有成功。
h5PayTest(){httpGet('/members/WxPayController/createOrder',{},res=>{// debugger// alert(res.data)// let data = JSON.parse(res.data)let data = res.data;alert(data.h5_url)location.href = data['h5_url']})}
这样你在安装了微信的手机上请求H5页面,然后发送请求打开链接就不会报错了。
然后就能完成支付了。
注意:
这里有几个坑
- 发送请求的参数一定要和文档一样,必填的填写好。否则下单会失败
- 你的请求必须是从添加的H5域名相同,否则下单链接可以获取,但是支付不能成功。有的小伙伴会问,我备案的域名试服务器的linux的,不能写代码,只能部署,总不能写一点部署一点,然后测试一点吧。其实只有申请的时候是需要备案的,申请通过之后,你可以在开发配置里面添加一个本地内网穿透的地址,例如花生壳或者netapp,亲测是可以配置上去的,然后穿透到你的eclipse 的server上, 然后不久可以开发了吗,上线了再把这个删掉。
- 参数的description字段中文会在请求显示的时候乱码,你可以转成utf8,这样写 StringEntity entity = new StringEntity(reqdata,Charset.forName("utf8"));
后面会涉及到notify和查询订单、对账单这些。我会在后面的过程中分享。
以及一些支付选择的策略模式我也会分享
我的代码和上面的优点出入,我是用springboot开发的,如果感兴趣我也可以再分享。
总之,这一步支付成功后,后面的功能都比较简单了。