JAVA微信支付和支付宝支付--App支付

张晔
2023-12-01

微信:微信商家开发平台进行审核、配置,拿到appid、商户号(mchid)、apiv3密钥、证书

支付宝:支付宝开放平台进行审核、配置,拿到支付宝公钥、商户私钥、appid

所需资料拿到后接下来集成到项目中
pom文件:

        <!-- 支付宝 -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.13.32.ALL</version>
        </dependency>

        <!-- 微信 -->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

微信支付:

1.工具类

/**
 * 微信支付V3版本
 */
@Component
public class WechatPayUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(WechatPayUtil.class);

    @Autowired
    private WechatPayConfig wechatPayConfig;

    /**
     * 微信支付V3-APP支付地址
     */
    private static final String APP_PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
    /**
     * 微信支付V3-小程序支付地址
     */
    private static final String JSAPI_PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    /**
     * 微信支付V3-申请退款地址
     */
    private static final String APP_REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    /**
     * 微信支付V3-APP商品回调地址
     */
    private static final String APP_PAY_NOTIFY_URL_GOODS = "/api/callback/paymentOfGoodsNotifyForWechatPay";

    /**
     * 商品退款回调地址
     */
    private static final String APP_REFUND_NOTIFY_URL_GOODS = "/cloud-order/api/callback/refundOfGoodsNotifyForWechatPay";
  

    /**
     * 微信小程序预支付
     *
     * @param outTradeNo 订单编号
     * @param amount     订单金额【分】
     */
    public PaymentResultDto wechatPayForJSAPI(PaymentCodeEnum paymentCodeEnum, String outTradeNo, BigDecimal amount, String openId) {
        // 绝对接口地址
        String canonicalURL = "/v3/pay/transactions/jsapi";
        // 订单总金额 元 -> 分
//        int totalAmount = amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
        // 构造异步回调地址
        String notifyURL = this.buildNotifyURL(paymentCodeEnum);
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        // 随机字符串
        String nonceStr = RandomUtil.randomString(32);
        // 加载证书信息
        KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
        // 构造请求体body
        String jsonBody = WechatFactory.buildPayBodyParamsForMini(wechatPayConfig.getMiniAppid(), wechatPayConfig.getMchid(), outTradeNo, notifyURL, totalAmount, openId);
        // 计算签名值
        String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
        // 构造HTTP头
        String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
        // 响应结果
        String responseBody = HttpRequest.post(JSAPI_PAY_URL).header("Authorization", authorization).body(jsonBody).execute().body();
        LOGGER.info("【WechatPay】responseBody = {}", responseBody);
        return this.buildMiniPrepayInfo(JSON.parseObject(responseBody).getString("prepay_id"), timestamp, nonceStr, pkcs12);
    }

    /**
     * 微信APP预支付
     *
     * @param outTradeNo 订单编号
     * @param amount     订单金额【分】
     */
    public PaymentResultDto wechatPayForAPP(PaymentCodeEnum paymentCodeEnum, String outTradeNo, BigDecimal amount) {
        // 绝对接口地址
        String canonicalURL = "/v3/pay/transactions/app";
        // 订单总金额 元 -> 分
        int totalAmount = amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
        System.out.println("totalAmount:"+totalAmount);
        // 构造异步回调地址
        String notifyURL = this.buildNotifyURL(paymentCodeEnum);
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        // 随机字符串
        String nonceStr = RandomUtil.randomString(32);
        //过期时间  RFC 3339格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        //支付订单过期时间
        String timeExpire = sdf.format(new Date(System.currentTimeMillis() + 30000));
        // 加载证书信息
        KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
        // 构造请求体body
        String jsonBody = WechatFactory.buildPayBodyParamsForAPP(wechatPayConfig.getAppid(), wechatPayConfig.getMchid(), outTradeNo, notifyURL, totalAmount, timeExpire);
        // 计算签名值
        String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
        // 构造HTTP头
        String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
        // 响应结果
        String responseBody = HttpRequest.post(APP_PAY_URL).header("Authorization", authorization).body(jsonBody).execute().body();
        LOGGER.info("【WechatPay】responseBody = {}", responseBody);
        return this.buildAppPrepayInfo(JSON.parseObject(responseBody).getString("prepay_id"), timestamp, nonceStr, pkcs12);
    }


    /**
     * 微信支付APP申请退款
     *
     * @param paymentCodeEnum 支付类型枚举
     * @param outTradeNo      原支付订单编号
     * @param outRefundNo     退款订单编号
     * @param reason          退款原因
     * @param refundAmount    退款金额(元)
     */
    public void wechatRefundForAPP(PaymentCodeEnum paymentCodeEnum, String outTradeNo, String outRefundNo, String reason, BigDecimal refundAmount, BigDecimal totalAmount) {
        // 绝对接口地址
        String canonicalURL = "/v3/refund/domestic/refunds";
        // 构造退款回调地址
        String refundNotifyURL = this.buildRefundNotifyURL(paymentCodeEnum);
        // 订单总金额 元 -> 分
       // int totalMoney = totalAmount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
        // 退款金额   元 -> 分
       // int refundMoney = refundAmount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
        int totalMoney =1;
        int refundMoney =1;
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        // 随机字符串
        String nonceStr = RandomUtil.randomString(32);
        // 加载证书信息
        KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
        // 构造请求体body参数
        String jsonBody = WechatFactory.buildRefundBodyParamsForAPP(outTradeNo, outRefundNo, refundNotifyURL, reason, refundMoney, totalMoney);
        LOGGER.info("【WechatRefund】body = {}", jsonBody);
        // 计算签名值
        String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
        // 构造HTTP头
        String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
        // 响应结果
        String responseBody = HttpRequest.post(APP_REFUND_URL).header("Authorization", authorization).body(jsonBody).execute().body();
        LOGGER.info("【WechatRefund】responseBody = {}", responseBody);
    }


    /**
     * 构造异步回调地址信息
     *
     * @param paymentCodeEnum 支付方式
     * @return String
     */
    private String buildNotifyURL(PaymentCodeEnum paymentCodeEnum) {
        String notifyURL = "";
        if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
            notifyURL = wechatPayConfig.getDomain() + APP_PAY_NOTIFY_URL_GOODS;
        } else if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
            notifyURL = wechatPayConfig.getDomain() + APP_PAY_NOTIFY_URL_LOCAL_LIFE;
        }
        return notifyURL;
    }


    /**
     * 构造退款回调地址
     *
     * @param paymentCodeEnum 支付类型枚举
     * @return the notify url
     */
    private String buildRefundNotifyURL(PaymentCodeEnum paymentCodeEnum) {
        String refundNotifyURL = "";
        if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
            refundNotifyURL = wechatPayConfig.getDomain() + APP_REFUND_NOTIFY_URL_GOODS;
        } else if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
            refundNotifyURL = wechatPayConfig.getDomain() + APP_REFUND_NOTIFY_URL_LOCAL_LIFE;
        }
        return refundNotifyURL;
    }


    /**
     * 返回APP预支付订单信息,再次封装返回前端
     *
     * @param prepayId 预支付订单id
     * @return PaymentResultDto
     */
    private PaymentResultDto buildAppPrepayInfo(String prepayId, long timestamp, String nonceStr, KeyPairFactory.KeyPairCustom pkcs12) {
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("appid", wechatPayConfig.getAppid());
        paramsMap.put("partnerid", wechatPayConfig.getMchid());
        paramsMap.put("prepayid", prepayId);
        paramsMap.put("packagestr", "Sign=WXPay");
        paramsMap.put("noncestr", nonceStr);
        paramsMap.put("timestamp", String.valueOf(timestamp));
        paramsMap.put("sign", SignFactory.signForAPP(wechatPayConfig.getAppid(), timestamp, nonceStr, prepayId, pkcs12));
        return JSON.parseObject(JSON.toJSONString(paramsMap), PaymentResultDto.class);
    }

    /**
     * 返回小程序预支付订单信息,再次封装返回前端
     *
     * @param prepayId 预支付订单id
     * @return PaymentResultDto
     */
    private PaymentResultDto buildMiniPrepayInfo(String prepayId, long timestamp, String nonceStr, KeyPairFactory.KeyPairCustom pkcs12) {
        timestamp = timestamp/ 1000L;
        Map<String, Object> keyValues = new HashMap();
        keyValues.put("packagestr", "prepay_id=" + prepayId);
        keyValues.put("noncestr", nonceStr);
        keyValues.put("timestamp", String.valueOf(timestamp));
        keyValues.put("sign", SignFactory.signForAPP(wechatPayConfig.getMiniAppid(), timestamp, nonceStr, "prepay_id=" + prepayId, pkcs12));
        keyValues.put("signType", "RSA");
        return JSON.parseObject(JSON.toJSONString(keyValues), PaymentResultDto.class);
    }

}
paymentCodeEnum参数是我自己设置的枚举,用来判断获取哪个支付地址。

KeyPairFactory:
/**
 * 微信支付V3
 * 获取密钥对信息
 */
public class KeyPairFactory {
    private static KeyStore store;
    private static final Object lock = new Object();
    /**
     * API证书路径【类路径/cert/apiclient_cert.p12】
     */
    private static final String KEY_PATH = "cert/apiclient_cert.p12";


    /**
     * 证书的别名(固定值:Tenpay Certificate)
     */
    private static final String KEY_ALIAS = "Tenpay Certificate";


    /**
     * 获取公私钥.
     *
     * @param keyPass 证书密码(默认:商户号)
     * @return the key pair
     */
    public static synchronized KeyPairCustom createPKCS12(String keyPass) {
        ClassPathResource resource = new ClassPathResource(KEY_PATH);

        char[] pem = keyPass.toCharArray();
        try {
            synchronized (lock) {
                if (store == null) {
                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(resource.getStream(), pem);
                    }
                }
            }
            X509Certificate certificate = (X509Certificate) store.getCertificate(KEY_ALIAS);
            certificate.checkValidity();
            // 证书的序列号 也有用
            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
            // 证书的 公钥
            PublicKey publicKey = certificate.getPublicKey();
            // 证书的 私钥
            PrivateKey storeKey = (PrivateKey) store.getKey(KEY_ALIAS, pem);
            return new KeyPairCustom(serialNumber, publicKey, storeKey);
        } catch (Exception e) {
            throw new IllegalStateException("Cannot load keys from store: " + resource, e);
        }
    }


    /**
     * 自定义返回密钥结果
     */
    @Data
    public static class KeyPairCustom {
        /**
         * API证书序列号
         */
        private String serialNo;
        /**
         * 证书的 公钥
         */
        private PublicKey publicKey;
        /**
         * 证书的 私钥
         */
        private PrivateKey privateKey;


        public KeyPairCustom(String serialNo, PublicKey publicKey, PrivateKey privateKey) {
            this.serialNo = serialNo;
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }
    }

}

config文件:

/**
 * 微信支付V3相关配置信息
 */
@Data
@Configuration
public class WechatPayConfig {

    /**
     * 域名
     */
    @Value("${pay.domain}")
    private String domain;

    /**
     * appid
     */
    @Value("${pay.wechat.appid}")
    private String appid;

    /**
     * miniAppid
     */
    @Value("${pay.wechat.miniAppid}")
    private String miniAppid;

    /**
     * 商户号
     */
    @Value("${pay.wechat.mchid}")
    private String mchid;

    /**
     * API V3 密钥
     */
    @Value("${pay.wechat.apiV3Key}")
    private String apiV3Key;

支付的时候调用WechatPayUtil 中的接口就可以了;

支付宝支付

工具类

@Component
public class AliPayUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(AliPayUtil.class);

    @Autowired
    private AliPayConfig aliPayConfig;

    // 商品异步通知回调地址
    private static final String GOODS_NOTIFY_URL = "/api/callback/paymentOfGoodsNotifyForAliPay";


    /**
     * APP商品预支付
     * @param outTradeNo 订单编号
     * @param totalAmount 订单金额【元】
     * @param subject 订单标题
     * @return 生成预支付响应对象返回前端
     */
    public String aliPayPrePaidForGoods(PaymentCodeEnum paymentCodeEnum, String outTradeNo, String totalAmount, String subject) {
        try {
            // 1. 创建AlipayClient实例
            DefaultAlipayClient alipayClient = getClientParams();
            // 2. 创建使用的Open API对应的Request请求对象
            AlipayTradeAppPayRequest request = getRequest(outTradeNo, totalAmount, subject);
            // 2.1 异步回调地址
            request.setNotifyUrl(this.buildNotifyURL(paymentCodeEnum));
            // 3. 发起请求并处理响应
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if (response.isSuccess()) return response.getBody();
            LOGGER.error("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
            throw new RuntimeException("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
        } catch (Exception e) {
            LOGGER.error("调用遭遇异常,原因:{}", e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
    }



    /**
     * 构造异步回调地址信息
     *
     * @param paymentCodeEnum 支付方式
     * @return String
     */
    private String buildNotifyURL(PaymentCodeEnum paymentCodeEnum) {
        String notifyURL = "";
        if (Objects.equals(PaymentCodeEnum.ALI_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
            notifyURL = aliPayConfig.getDomain() + GOODS_NOTIFY_URL;
        } else if (Objects.equals(PaymentCodeEnum.ALI_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
            notifyURL = aliPayConfig.getDomain() + LOCAL_LIFE_NOTIFY_URL;
        }
        return notifyURL;
    }



    private DefaultAlipayClient getClientParams() {
        return new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", aliPayConfig.getAppid(), aliPayConfig.getMerchantPrivateKey(), "json", "utf-8", aliPayConfig.getAlipayPublicKey(), "RSA2");
    }

    private AlipayTradeAppPayRequest getRequest(String outTradeNo, String totalAmount, String subject) {
        // 初始化Request,并填充Model属性。实际调用时请替换为您想要使用的API对应的Request对象。
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        AlipayTradeAppPayModel payModel = new AlipayTradeAppPayModel();
        //订单编号
        payModel.setOutTradeNo(outTradeNo);
        //订单金额  单位:【元】,精确到小数点后两位
        payModel.setTotalAmount(totalAmount);
        //订单标题
        payModel.setSubject(subject);
        request.setBizModel(payModel);
        return request;
    }

    /**
     * 服务端验证异步通知信息参数示例
     * @param request 请求对象
     * @return Map
     */
    public Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        return params;
    }

    /**
     * 验证异步通知信息参数
     * @param params 参数集合
     * @return boolean
     */
    public boolean rsaCertCheckV1(Map<String, String> params) {
        //切记alipayPublicCertPath是支付宝公钥证书路径,请去open.alipay.com对应应用下载。
        try {
            return AlipaySignature.rsaCheckV1(params, aliPayConfig.getAlipayPublicKey(), "utf-8","RSA2");
        } catch (AlipayApiException e) {
            LOGGER.error("验证异步通知信息参数失败,Message = {}", e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

}

config文件

@Data
@Configuration
public class AliPayConfig {

    /**
     * 主域名
     */
    @Value("${pay.domain}")
    private String domain;
    /**
     * 应用ID
     */
    @Value("${pay.alipay.appid}")
    private String appid;
    /**
     * 商户私钥,您的PKCS8格式RSA2私钥
     */
    @Value("${pay.alipay.privateKey}")
    private String merchantPrivateKey;
    /**
     * 公钥
     */
    @Value("${pay.alipay.publicKey}")
    private String alipayPublicKey;

}

 类似资料: