微信jsapi支付保姆级流程

目录

目录

一、微信支付参数

1.商户号

2.公众号ID

3.商户号绑定公众号

4.证书和秘钥

5.注意事件

二、微信SDK

三、获取openID

1.获取code

2.获取openid

四、下载微信支付证书

五、代码

1.配置微信支付参数

2.配置微信支付配置类

3.封装返回请求

4.微信统一下单

5.方法实现

总结


一、微信支付参数

1.商户号

微信支付平台->账号中心->登录账号。

2.公众号ID

微信公众号平台->设置与开发->基本配置->开发者ID。

3.商户号绑定公众号

微信支付平台->产品中心->AppID账号管理->关联AppID。

AppID:公众号ID。
AppID主体:微信公众号平台->设置与开发->公众号设置->主体信息。

4.证书和秘钥

微信支付平台->账号中心->API安全。

使用v2就申请APIv2秘钥,使用v3就申请APIv3秘钥,随机密码生成的网站在这,秘钥申请完毕。

点下载证书工具,安装打开,证书保存路径,填入商户号和商户名称,刚刚你点下载证书工具的那个页面就有这两个参数,下一步,复制出现的请求串,换到微信支付平台,粘贴复制的请求串,下一步,输入操作密码,复制出来的证书串,打开证书生成工具,下一步,这个会自动粘贴上去,下一步,证书生成成功。

5.注意事件

证书文件后面是要加入到项目解密的,在微信SDK中已经做好了解密的代码,可能会出现解密失败,是因为jdk不支持256的解密,是需要修改两个配置文件的,jdk1.8.0_131\jre\lib\security中的local_policy.jar和US_export_policy.jar。去官方下载支持256位解密的,这里我已经提供了,出现解密失败自行下载。

二、微信SDK

<dependency>
     <groupId>com.github.wechatpay-apiv3</groupId>
     <artifactId>wechatpay-apache-httpclient</artifactId>
     <version>0.4.8</version>
</dependency>

微信SDK地址

三、获取openID

官方文档

1.获取code

官方示例如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D42030519460&response_type=code&scope=snsapi_base&state=123#wechat_redirect

appid->上面获取到的公众号id,

redirect_uri->用 urlEncode 对链接进行处理,URLEncoder.encode(redirect_uri),我推荐是用前端页面地址,我就是用了我公众号版本的首页的地址,用户每次进入时就会获取code。

2.获取openid

官方示例如下:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
 

appid->公众号id, SECRET ->公众号密钥

前端获取到的code发送到后端,然后发送请求,获取到openId。

四、下载微信支付证书

CertificateDownloader.jar下载后,名字带版本号,我这里没写,把jar包名字改一下或者命令里面名字改一下都可再cmd。

java -jar CertificateDownloader.jar 
-k 证书解密的密钥 
-m 商户号 
-s 商户证书的序列号 
-o 保存证书的路径 
-f 商户的私钥文件路径

把命令执行,即可下载微信证书,在把证书放到项目根目录里面,运行项目一次后,即可删除。

五、代码

1.配置微信支付参数

resources - > application.properties。

# 商户号
wxpay.mch-id=
# 证书号,申请api证书里面
wxpay.mch-serial-no=
# 商户私钥文件 申请的证书文件里面的
wxpay.private-key-path=apiclient_key.pem
# Apiv3 秘钥 上面你申请的证书时设置的
wxpay.api-v3-key=
# Appid
wxpay.appid=

2.配置微信支付配置类

config -> WxpayConfig。

package com.dingyutx.config;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;


@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data
public class WxPayConfig {

    /**
     * 商户号
     */
    private String mchId;

    /**
     * 商户API证书序列号
     */
    private String mchSerialNo;

    /**
     * 商户私钥文件地址
     */
    private String privateKeyPath;

    /**
     * APIv3密钥
     */
    private String apiV3Key;

    /**
     * APPID
     */
    private String appid;

    /**
     *获取商户秘钥
     *@return PrivateKey
     */
    public PrivateKey getPrivateKey(String filename){
        try{
            return PemUtil.loadPrivateKey(new FileInputStream(filename));
        } catch (FileNotFoundException e){
            throw new RuntimeException("秘钥文件不存在",e);
        }
    }

    /**
     *获取微信平台签名认证器
     *@return Verifier
     */
    @Bean
    public Verifier getVerifier() throws Exception {
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        //获取签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo,privateKey);
        //身份认证对象
        WechatPay2Credentials wechatPay2Credentials =new WechatPay2Credentials(mchId ,privateKeySigner);
        //定时刷新微信平台的签名认证
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        return certificatesManager.getVerifier(mchId);
    }

    /**
     *获取http请求对象
     *@return CloseableHttpClient
     */
    @Bean
    public CloseableHttpClient getWxpayClient(Verifier verifier){
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
        //CloseableHttpResponse response = httpClient.execute();
        return builder.build();
    }
}

3.封装返回请求

entity->vo->R

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.HashMap;
import java.util.Map;

@Data
@Accessors(chain = true)
public class R {
    private Integer code; //响应码
    private String message; //响应消息
    private Map<String,Object> data = new HashMap<>(); //响应数据

    public static R ok(){
        R r =new R();
        r.setCode(0);
        r.setMessage("success");
        return r;
    }

    public static R error(){
        R r =new R();
        r.setCode(-1);
        r.setMessage("defeat");
        return r;
    }

    public R data(String key, Object value){
        this.data.put(key, value);
        return this;
    }
}

4.微信统一下单

controller -> WxPayController。

注意事件:Verifier 导入需要配置完WxpayConfig重启一下,下载证书。

package com.dingyutx.controller;

import com.dingyutx.config.WxPayConfig;
import com.dingyutx.entity.OrderInfo;
import com.dingyutx.service.WxPayService;
import com.dingyutx.util.HttpUtils;
import com.dingyutx.util.WechatPay2ValidatorForRequest;
import com.dingyutx.vo.R;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;


@CrossOrigin
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付API")
@Slf4j
public class WxPayController{

    @Resource
    private WxPayService wxPayService;

    @Resource
    private WxPayConfig wxPayConfig;

    @Resource
    private Verifier verifier;

    @ApiOperation("调用统一下单API,生成预支付交易会话标识")
    @PostMapping("/jsapi")
    public R jsapiPay(OrderInfo orderInfo, String openid) throws Exception{
        log.info("发起支付请求");
        Map<String,Object> map = wxPayService.jsapiPay(orderInfo,openid);
        return R.ok().setData(map);
    }

    @ApiOperation("微信支付回调")
    @PostMapping("/jsapi/notify")
    public R jsapiNotify(HttpServletRequest req, HttpServletResponse res){
        try {
            String body = readData(req);
            // 构建request,传入必要参数
            NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(req.getHeader("Wechatpay-Serial"))
                    .withNonce(req.getHeader("Wechatpay-Nonce"))
                    .withTimestamp(req.getHeader("Wechatpay-Timestamp"))
                    .withSignature(req.getHeader("Wechatpay-Signature"))
                    .withBody(body)
                    .build();
            NotificationHandler handler = new NotificationHandler(verifier, wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            Notification notification = handler.parse(request);
            // 从notification中获取解密报文
            String decryptData = notification.getDecryptData();
            //开始你的业务逻辑

            res.setStatus(200);
            return R.ok();
        }catch (Exception e){
            throw new RuntimeException("解密失败===>"+e.getMessage());
        }
    }

    /**
     * 读取微信支付信息
     * @param request 信息
     * @return 结果
     */
    private static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

5.方法实现

package com.dingyutx.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.dingyutx.config.WxPayConfig;
import com.dingyutx.entity.OrderInfo;
import com.dingyutx.service.WxPayService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class WxPayServiceImpl implements WxPayService {

    @Resource
    private WxPayConfig wxPayConfig;

    @Resource
    private CloseableHttpClient httpClient;

    /**
     * jsapi用户下单
     * @param orderInfo 订单信息
     * @param openid 微信用户标识
     * @return 预支付标识
     */
    @Override
    public Map<String, Object> jsapiPay(OrderInfo orderInfo,String openid) {
        //调用统一下单API
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");
        try {
            //微信支付参数
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid",wxPayConfig.getMchId())
                    .put("appid", wxPayConfig.getAppid())
                    .put("description", orderInfo.getTitle())
                    .put("notify_url", "回调链接自己写")
                    .put("out_trade_no", orderInfo.getOrderNo());
            rootNode.putObject("amount")
                    .put("total", orderInfo.getTotalFee());
            rootNode.putObject("payer")
                    .put("openid", openid);
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //获取对于的微信支付认证并发送请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            //微信支付返回信息
            String bodyAsString = EntityUtils.toString(response.getEntity());
            String prepayId = JSONObject.parseObject(bodyAsString).getString("prepay_id");
            //返回调起支付的信息
            return getPayment("prepay_id="+prepayId, wxPayConfig.getAppid(),wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
        } catch (Exception e) {
            throw new RuntimeException("支付失败"+e.getMessage());
        }
    }

    /** 获取微信支付参数信息 */
    private Map<String,Object> getPayment(String prepayId, String appId, PrivateKey privateKey) {
        Map<String,Object> map = new HashMap<>();
        String nonceStr = UUID.randomUUID().toString().toUpperCase();
        long timeStamp = System.currentTimeMillis()/1000;
        String source=appId+"\n"+timeStamp+"\n"+nonceStr+"\n"+prepayId+"\n";
        String sign = getSign(source.getBytes(StandardCharsets.UTF_8), privateKey);
        map.put("appId", appId);
        map.put("timeStamp", timeStamp);
        map.put("nonceStr", nonceStr);
        map.put("package", prepayId);
        map.put("signType", "RSA");
        map.put("paySign", sign);
        return map;
    }

    /** 获取微信微信支付签名 */
    private String getSign(byte[] message, PrivateKey yourPrivateKey) {
        try {
            // 签名方式(固定SHA256withRSA)
            Signature sign = Signature.getInstance("SHA256withRSA");
            // 使用私钥进行初始化签名(私钥需要从私钥文件【证书】中读取)
            sign.initSign(yourPrivateKey);
            // 签名更新
            sign.update(message);
            // 对签名结果进行Base64编码
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            throw new RuntimeException("获取微信支付签名失败");
        }
    }

}

总结

花费三天时间把微信jsapi支付搞完了,记录一下,0407更新的更简单了。

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
### 回答1: 微信JSAPI支付是一种使用微信内置浏览器或微信客户端内的网页进行支付的方式。流程如下: 1. 用户在商户的网站或应用内点击支付按钮,触发支付请求。 2. 商户服务器收到支付请求后,向微信支付服务器发起下单请求,并将支付信息传递给微信支付服务器。 3. 微信支付服务器收到下单请求后,会根据商户提交的信息生成支付二维码或支付链接。 4. 商户服务器收到微信支付服务器的响应后,将支付二维码或支付链接发送给用户浏览器。 5. 用户浏览器打开支付二维码或支付链接,跳转到微信内置浏览器或微信客户端进行支付。 6. 用户在微信内置浏览器或微信客户端内输入支付密码或通过其他认证方式进行支付。 7. 微信支付服务器收到用户的支付信息后,会向商户服务器发送支付通知。 8. 商户服务器收到支付通知后,会对订单进行处理,并向用户 ### 回答2: 微信JSAPI支付是指通过微信公众号或小程序内的网页调用微信支付接口实现支付功能的流程。 1. 用户点击支付按钮后,公众号或小程序调用微信支付接口向微信服务器发起支付请求。 2. 微信服务器返回预支付交易会话标识prepay\_id给公众号或小程序。 3. 公众号或小程序将prepay\_id和其他支付参数通过JSAPI方式传递给前端页面。 4. 前端页面通过微信JSAPI提供的支付接口,调用微信客户端进行支付。 5. 用户在微信客户端确认支付后,微信服务器会将支付结果通知给公众号或小程序服务器。 6. 公众号或小程序服务器接收到支付结果通知后,进行订单处理,更新订单状态。 7. 公众号或小程序服务器向微信服务器返回处理结果,微信服务器会将处理结果通知给微信客户端。 8. 微信客户端收到支付结果通知后,提示支付成功或失败,并跳转至指定页面。 整个过程中,涉及到的主要步骤有:发起支付请求、获取预支付交易会话标识、传递参数给前端、调用微信客户端支付、接收支付结果通知、处理订单、返回处理结果给微信服务器、微信客户端显示支付结果。 微信JSAPI支付能够为公众号或小程序提供方便、安全的支付体验,使用户能够快速完成支付操作,同时也提供了支付结果的即时通知和订单处理的接口,方便商户进行后续操作。 ### 回答3: 微信JSAPI支付流程是指通过微信公众号或小程序内页面上的网页进行支付流程。具体流程如下: 1. 用户在微信公众号或小程序内选择商品并确认下单。 2. 商户服务器将订单信息提交给微信服务器,包括商品描述、订单号、金额等信息,并生成预支付交易会话标识prepay_id。 3. 商户服务器将prepay_id返回给前端页面。 4. 前端页面调用微信JSAPI的支付接口,传入prepay_id以及其他支付相关参数(如时间戳、随机字符串等)。 5. 微信服务器校验参数,并生成支付签名。 6. 微信服务器将支付签名返回给前端页面。 7. 前端页面调用微信JSAPI的支付接口,传入支付签名以及其他支付相关参数。 8. 用户在微信客户端确认支付并输入支付密码。 9. 微信服务器接收支付结果,并返回给商户服务器。 10. 商户服务器校验支付结果,更新订单状态,并返回相应的支付结果给用户。 需要注意的是,为了确保支付的安全性,商户服务器需要对支付结果进行校验,并对订单状态进行更新,以防止重复支付支付金额被修改等情况的发生。 以上就是微信JSAPI支付的主要流程微信JSAPI支付相对于其他支付方式来说,使用便捷,支持在微信内进行快速支付,适用于微信公众号和小程序等场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

予风北

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值