微信H5支付(V3)

最近在做一个涉及支付的项目,其中对支付模块的内容涉及的比较多,记录一点心得。如果有错误,希望各位大佬斧正。

这个支付项目涉及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"));
 
    // 初始化httpClient
    httpClient = 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页面,然后发送请求打开链接就不会报错了。

然后就能完成支付了。

注意:

这里有几个坑

  1. 发送请求的参数一定要和文档一样,必填的填写好。否则下单会失败
  2. 你的请求必须是从添加的H5域名相同,否则下单链接可以获取,但是支付不能成功。有的小伙伴会问,我备案的域名试服务器的linux的,不能写代码,只能部署,总不能写一点部署一点,然后测试一点吧。其实只有申请的时候是需要备案的,申请通过之后,你可以在开发配置里面添加一个本地内网穿透的地址,例如花生壳或者netapp,亲测是可以配置上去的,然后穿透到你的eclipse 的server上, 然后不久可以开发了吗,上线了再把这个删掉。
  3. 参数的description字段中文会在请求显示的时候乱码,你可以转成utf8,这样写    StringEntity entity = new StringEntity(reqdata,Charset.forName("utf8"));

后面会涉及到notify和查询订单、对账单这些。我会在后面的过程中分享。

以及一些支付选择的策略模式我也会分享

我的代码和上面的优点出入,我是用springboot开发的,如果感兴趣我也可以再分享。

总之,这一步支付成功后,后面的功能都比较简单了。

以下是一个 Java 版本的微信 H5 支付 V3 API 异步通知的示例代码: ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Base64; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class WeChatH5NotifyHandler { private static final String WECHAT_API_CERT_SERIAL_NUMBER = "YOUR_WECHAT_API_CERT_SERIAL_NUMBER"; private static final String WECHAT_API_CERTIFICATE = "YOUR_WECHAT_API_CERTIFICATE"; private static final String WECHAT_API_CERTIFICATE_PRIVATE_KEY = "YOUR_WECHAT_API_CERTIFICATE_PRIVATE_KEY"; public static void handleNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { try { InputStream inputStream = request.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder requestBody = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { requestBody.append(line); } // 验证通知的签名 String wechatSerialNumber = request.getHeader("Wechatpay-Serial"); String wechatSignature = request.getHeader("Wechatpay-Signature"); String wechatTimestamp = request.getHeader("Wechatpay-Timestamp"); String wechatNonce = request.getHeader("Wechatpay-Nonce"); if (verifySignature(wechatSerialNumber, wechatSignature, wechatTimestamp, wechatNonce, requestBody.toString())) { // 验证签名成功 // 处理支付成功的逻辑 // ... // 返回成功响应给微信服务器 response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write("OK"); } else { // 验证签名失败,返回失败响应给微信服务器 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.getWriter().write("Signature verification failed."); } } catch (Exception e) { e.printStackTrace(); // 返回失败响应给微信服务器 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.getWriter().write("Internal Server Error"); } } private static boolean verifySignature(String wechatSerialNumber, String wechatSignature, String wechatTimestamp, String wechatNonce, String requestBody) throws Exception { String message = wechatTimestamp + "\n" + wechatNonce + "\n" + requestBody + "\n"; String signature = "SHA256withRSA"; X509Certificate certificate = getCertificate(); certificate.checkValidity(); certificate.verify(certificate.getPublicKey()); return true; // 假设签名验证成功 } private static X509Certificate getCertificate() throws CertificateException { byte[] certBytes = Base64.getDecoder().decode(WECHAT_API_CERTIFICATE.getBytes(StandardCharsets.UTF_8)); CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); InputStream inputStream = new ByteArrayInputStream(certBytes); return (X509Certificate) certificateFactory.generateCertificate(inputStream); } } ``` 在上述代码中,我们创建了一个名为 `WeChatH5NotifyHandler` 的类,其中的 `handleNotify` 方法用于处理微信 H5 支付 V3 API 的异步通知。该方法接收 `HttpServletRequest` 和 `HttpServletResponse` 对象作为参数,从请求中获取异步通知的数据,并进行相应的处理逻辑。 在 `handleNotify` 方法中,我们首先获取请求中的数据,并从请求头中获取签名相关信息(`Wechatpay-Serial`、`Wechatpay-Signature`、`Wechatpay-Timestamp`、`Wechatpay-Nonce`)。然后,我们使用 `verifySignature` 方法验证通知的签名是否正确。在示例代码中,我们假设签名验证成功。 如果签名验证成功,则表示支付成功,可以进行相应的处理逻辑,并返回成功响应给微信服务器。如果签名验证失败,则返回失败响应给微信服务器。 请注意,真实的微信 H5 支付 V3 API 的异步通知处理可能涉及到与微信服务器的交互、密钥管理、验签等复杂操作,需要仔细阅读微信支付文档,并使用微信提供的 SDK 或工具类来简化开发。以上示例代码仅供参考,需要根据实际情况进行修改和完善。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值