保姆级微信小程序jsapiV3支付开荒

→【传送门】参考大佬的代码讲解,感谢大佬←

application.xml配置

# 微信配置
wechat:
  #appId
  appId: wx...
  #appSecret
  appSecret: 5d0c...
  #商户号
  merchantId: 168...
  # 私钥路径
  privateKeyPath: H:\work\...\apiclient_key.pem
  # 商户号证书序列号
  merchantSerialNumber: 601B...
  # 商户apiV3秘钥
  apiV3key: 888888...
  #支付回调
  notifyUrl: https://..../wechatPayCallback

登录微信支付商户平台
商户号这里找↓

在这里插入图片描述

然后申请api证书和设置apiv3秘钥,照着步骤来即可

一定要先申请证书再设置秘钥!一定要先申请证书再设置秘钥!一定要先申请证书再设置秘钥!

在这里插入图片描述
申请证书后本地生成一个压缩文件,解压后找个地方放,私钥路径是apiclient_key.pem的路径
在这里插入图片描述
点击管理证书,复制证书序列号
在这里插入图片描述

在这里插入图片描述
添加支付授权目录,必须是外网能访问的域名
如果路径是/smallapp/pay/wechatPayCallback,写到/smallapp/pay就可以了
在这里插入图片描述

引入sdk

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.12</version>
</dependency>

新建WechatPayService.java

@Slf4j
@Component
public class WechatPayService {
}

私有化下列属性,给个@Getter方便外部调用

    @Getter
    private Config config;

    @Getter
    private JsapiServiceExtension jsapiServiceExtension;

    @Getter
    @Value("${wechat.appId}")
    private String appId;

    @Getter
    @Value("${wechat.merchantId}")
    private String merchantId;

    @Getter
    @Value("${wechat.privateKeyPath}")
    private String privateKeyPath;

    @Getter
    @Value("${wechat.merchantSerialNumber}")
    private String merchantSerialNumber;

    @Getter
    @Value("${wechat.apiV3key}")
    private String apiV3key;

    @Getter
    @Value("${wechat.notifyUrl}")
    private String notifyUrl;

初始化config和jsapiServiceExtension

    @PostConstruct
    private void init(){
        config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3key)
                .build();
        jsapiServiceExtension = new JsapiServiceExtension.Builder()
                .config(config)
                .signType("RSA")
                .build();
    }

支付接口处理、支付回调处理


	private static final int TAG_LENGTH_BIT = 128;

    public <T> T payExecute(Supplier<T> supplier){
        try {
            T t = supplier.get();
            log.info("调用微信支付接口返回: {}", JSON.toJSONString(t));
            return t;
        } catch (HttpException e){
            log.error("调用微信支付接口 HttpException: {}", e.getMessage());
            throw  new com.ruoyi.common.exception.ServiceException("系统错误");
        } catch (ServiceException e){
            log.error("调用微信支付接口 ServiceException: {}", e.getMessage());
            throw  new com.ruoyi.common.exception.ServiceException("系统错误");
        } catch (MalformedMessageException e){
            log.error("调用微信支付接口 MalformedMessageException: {}", e.getMessage());
            throw  new com.ruoyi.common.exception.ServiceException("系统错误");
        }
    }

    public Boolean notifyExecute(JSONObject jsonObject, Consumer<String> consumer){

        log.info("[notifyExecute] 支付回调: {}", jsonObject.toJSONString());

        String outTradeNo = "";

        try {
            JSONObject resource = jsonObject.getJSONObject("resource");
            String associatedData = resource.getString("associated_data");
            String ciphertext = resource.getString("ciphertext");
            String nonce = resource.getString("nonce");

            String decryptData = decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);

            JSONObject decryptDataJobj = JSONObject.parseObject(decryptData);
            if("SUCCESS".equalsIgnoreCase(decryptDataJobj.getString("trade_state"))){
                outTradeNo = decryptDataJobj.getString("out_trade_no");
                log.info("支付成功 商户订单id: {}", outTradeNo);
                try {
                    consumer.accept(outTradeNo);
                } catch (Exception e) {
                    log.info(" 商户订单id: {} 业务失败 e: {}", outTradeNo, e);
                    return Boolean.FALSE;
                }
            }
        } catch (Exception e) {
            log.info("支付回调: {} 解析回调参数失败 e: {}", jsonObject.toJSONString(), e);
        }

        return Boolean.TRUE;
    }

    String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext){
        try {

            Cipher cipher = Cipher.getInstance("AES/GCM/noPadding");

            SecretKey key = new SecretKeySpec(apiV3key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "UTF-8");

        }catch (NoSuchAlgorithmException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (NoSuchPaddingException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (InvalidAlgorithmParameterException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (UnsupportedEncodingException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (IllegalBlockSizeException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (BadPaddingException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        } catch (InvalidKeyException e) {
            throw new com.ruoyi.common.exception.ServiceException(e.getMessage());
        }

    }

调用支付的Controller

@RequestMapping("/smallapp/pay")
@RequiredArgsConstructor
public class PayController extends BaseController {

    private final WechatPayService wechatPayService;
    
    ...

}

组装预下单参数,调用预下单

	PrepayRequest request = new PrepayRequest();
	// 组装预下单参数,其他参数有需要的看着来
	request.setAmount(支付金额,单位为分);
	request.setAppid(wechatPayService.getAppId());
    request.setMchid(wechatPayService.getMerchantId());
	request.setOutTradeNo(唯一订单编号);
	request.setNotifyUrl(wechatPayService.getNotifyUrl());
	request.setTimeExpire(支付到期时间);
	...

	//支付用户的openid
	Payer payer = new Payer();
    payer.setOpenid(user.getOpenId());
    request.setPayer(payer);

    var resp = wechatPayService.payExecute(() ->
            wechatPayService.getJsapiServiceExtension()
                    .prepayWithRequestPayment(request)
    );

	return success(resp);

uniapp

prepayByWechat({...}).then(res => {
	wx.requestPayment({
		timeStamp: res.data.timeStamp,
		nonceStr: res.data.nonceStr,
		package: res.data.packageVal,
		signType: res.data.signType,
		paySign: res.data.paySign,
		success: (suc) => {
			//微信支付成功
		},
		fail: (err) => {
			//微信支付失败
		}
	})
})

支付回调 注意是post请求

    @PostMapping("/wechatPayCallback")
    @ResponseBody
    public AjaxResult wechatPayCallback(@RequestBody JSONObject jsonObject){

        var notifyExecute = wechatPayService.notifyExecute(jsonObject, outTradeNo -> {

            //处理业务逻辑(outTradeNo唯一订单编号),处理失败要抛业务异常!
            ...

        });
        //成功返回200,失败返500
        return notifyExecute? success(): error();
    }

一套微信支付的祖传代码就完成了→_→

微信那个文档写得真拉,yue

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值