支付宝、微信v3、PayPal支付接入[Java][SpringBoot]

目录

基本概述

支付接入

支付宝

APP支付

移动端网页支付

PC端支付

微信

APP支付

移动端网页支付

小程序支付

PC端网页支付

PayPal(代码已完成,流程待补充)

订单支付

创建订阅(订阅支付)


基本概述

        我把做过的第三方支付封装成“拿来即用”的Demo。

        文章内容尽量不讲废话,只讲“是什么”,不讲“为什么”。

        包括:

                支付宝支付:[APP支付]、[移动端网页支付]、[PC端支付]

                微 信 支 付 :[APP支付]、[移动端网页支付]、[PC端网页支付]、[小程序支付]

                PayPal支付:[订单支付] 、[订阅支付]  

代码地址(重点):https://github.com/scorpion-lailai/pay-sdk-demo.git

支付逻辑不懂的看这里:聚合支付、单商户多商户支付、微信/支付宝/PayPal支付流程、支付政策法规_「蝎子莱莱」的博客-CSDN博客聚合支付、单商户和多商户支付、[支付宝/微信]APP支付、[支付宝/微信]H5支付、[微信]PC支付、[支付宝]PC支付、[微信]小程序支付、PayPal支付、支付政策法规https://blog.csdn.net/qq_39906413/article/details/127802966?spm=1001.2014.3001.5502


支付接入

支付宝

需要的配置信息:       
        pay.config.alipay.appid=支付宝APPid
        pay.config.alipay.private.key=你的privateKey
        pay.config.alipay.public.key=你的publicKey
        pay.config.alipay.sign.type=RSA2

APP支付

截图

流程:

        1. 填写[订单号]和[订单金额],请求接口

        2.返回客户端支付调用需要的参数,客户端拿到参数后,可以直接调起支付

后端参照及接口说明:app支付接口2.0 | 网页&移动应用

代码:

 public static String APP(Long orderId, BigDecimal amount, NotifyUrl notifyUrl) {
        // 设置请求的参数
        JSONObject json = new JSONObject();
        json.put("out_trade_no", orderId.toString());
        json.put("total_amount", amount.toString());
        json.put("subject", "APP在线支付");
        json.put("product_code", "QUICK_MSECURITY_PAY");

        AlipayClient alipayClient = AliPaySingleTransfer.getAlipayClient();
        AlipayTradeAppPayRequest alipayRequest = new AlipayTradeAppPayRequest();

        // 回调服务端URL
        alipayRequest.setNotifyUrl(CURRENT_ENV_URL + notifyUrl.getUrl());
        alipayRequest.setBizContent(json.toJSONString());
        // 请求
        String result;
        try {
            result = alipayClient.sdkExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
            throw new ResultException(Errors.ERROR);
        }
        return result;
    }

移动端网页支付

截图

流程:

        1. 填写[订单号]和[订单金额]和[支付成功后前端页面跳转地址],请求接口

        2.返回客户端信息为[form表单]

        3. 客户端把[form表单]放到页面上后,再提交表单,即可跳转支付页面

后端参照及接口说明:手机网站支付接口2.0 | 网页&移动应用

代码:

 public static String H5(Long orderId, BigDecimal amount, String returnUrl, NotifyUrl notifyUrl) {

        // 设置请求的参数
        JSONObject json = new JSONObject();
        json.put("out_trade_no", orderId.toString());
        json.put("total_amount", amount.toString());
        json.put("subject", "移动H5在线支付");
        json.put("product_code", "QUICK_WAP_WAY");

        AlipayClient alipayClient = AliPaySingleTransfer.getAlipayClient();
        AlipayTradeWapPayRequest wap = new AlipayTradeWapPayRequest();

        // 回调服务端URL
        wap.setReturnUrl(returnUrl);
        wap.setNotifyUrl(CURRENT_ENV_URL + notifyUrl.getUrl());
        wap.setBizContent(json.toJSONString());
        // 请求
        String result;
        try {
            result = alipayClient.pageExecute(wap).getBody();

        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
            throw new ResultException(Errors.ERROR);
        }
        return result;
    }

PC端支付

截图

流程:

        1. 填写[订单号]和[订单金额]和[支付成功后前端页面跳转地址],请求接口

        2.返回客户端信息为[form表单]

        3. 客户端把[form表单]放到页面上后,再提交表单,即可跳转支付页面

代码:        

 public static String PC(Long orderId, BigDecimal amount, String returnUrl, NotifyUrl notifyUrl) {
        // 设置请求的参数
        JSONObject json = new JSONObject();
        json.put("out_trade_no", orderId);
        json.put("total_amount", amount);
        json.put("subject", "PC端在线支付");
        json.put("body", "无");
        json.put("product_code", "FAST_INSTANT_TRADE_PAY");

        AlipayClient alipayClient = AliPaySingleTransfer.getAlipayClient();
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

        // 支付成功后,页面跳转
        alipayRequest.setReturnUrl(returnUrl);
        // 回调服务端URL
        alipayRequest.setNotifyUrl(CURRENT_ENV_URL + notifyUrl.getUrl());
        alipayRequest.setBizContent(json.toJSONString());
        // 请求
        String result;
        try {
            result = alipayClient.pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);
            throw new ResultException(Errors.ERROR);
        }
        return result;
    }

微信

需要的配置信息:
        #商户号
        pay.config.wechat.mchid=你的商户号
        #商户私钥文件
        pay.config.wechat.privateKey.path=商户私钥文件路径
        #平台证书
        pay.config.wechat.cert.path=平台证书文件路径
        #商户平台的v3Key
        pay.config.wechat.v3Key=v3Key
        #证书序列号
        pay.config.wechat.serialNo=序列号
        #app支付的AppId
        pay.config.wechat.app.appid=AppId
        #pc支付的AppId
        pay.config.wechat.pc.appid=AppId
        #移动端网页支付的AppId
        pay.config.wechat.h5.appid=AppId
        #小程序支付的AppId
        pay.config.wechat.miniProgram.appid=AppId

       [平台证书]生成方式: https://github.com/wechatpay-apiv3/CertificateDownloader

APP支付

截图

流程:

        1. 填写[订单号]和[订单金额],请求接口

        2.返回客户端支付调用需要的参数,客户端拿到参数后,可以直接调起支付

后端参照及接口说明:微信支付-开发者文档

前端参照及接口说明:微信支付-开发者文档

代码:

 public static Map<String, String> APP(Long orderId, BigDecimal amount, NotifyUrl notifyUrl) {
        String APPID = APPID_APP;

        CloseableHttpClient httpClient = buildHttp();
        HttpPost httpPost = buildHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode
                .put("mchid", mchId)
                .put("appid", APPID)
                .put("description", "APP在线付费")
                .put("notify_url", CURRENT_ENV_URL + notifyUrl.getUrl())
                .put("out_trade_no", orderId.toString());

        amount = (amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP));
        rootNode.putObject("amount").put("total", amount.intValue());
        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            // http repsonse
            HttpEntity entity = response.getEntity();
            String s = EntityUtils.toString(entity);
            log.info("返回参数为:{}", s);

            JSONObject json = JSON.parseObject(s);
            String prepayId = json.getString("prepay_id");

            // 拼接参数返回APP
            String nonce = generateNonceStr();
            long timeStamp = System.currentTimeMillis() / 1000;

            Map<String, String> result = new HashMap<>();
            result.put("appId", APPID); // 应用appid
            result.put("nonceStr", nonce);
            result.put("timestamp", Long.toString(timeStamp));
            result.put("prepayId", prepayId); // 返回App

            String sign =
                    PrivateKeySign.getToken(APPID, prepayId, nonce, timeStamp, getPrivateKey(privateKeyPath));

            // 签名
            result.put("sign", sign); // 再次签名
            result.put("package", "Sign=WXPay");
            result.put("partnerId", mchId); // 商户号
            return result;
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new ResultException(Errors.ERROR);
        }
    }

移动端网页支付

截图

流程:

        1. 填写[订单号]和[订单金额],请求接口

        2.返回客户端一个url链接,客户端打开链接后,可以直接调起支付

后端参照及接口说明:微信支付-开发者文档

前端参照及接口说明:微信支付-开发者文档

public static Map<String, String> H5(Long orderId, BigDecimal amount, NotifyUrl notifyUrl) {
        CloseableHttpClient httpClient = buildHttp();
        HttpPost httpPost = buildHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/h5");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode
                .put("mchid", mchId)
                .put("appid", APPID_H5)
                .put("description", "移动H5在线付费")
                .put("notify_url", CURRENT_ENV_URL + notifyUrl.getUrl())
                .put("out_trade_no", orderId.toString());

        amount = (amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP));
        rootNode.putObject("amount").put("total", amount.intValue());
        ObjectNode sceneInfo = rootNode.putObject("scene_info");
        sceneInfo.put("payer_client_ip", IPUtils.getIpAddress());

        ObjectNode h5InfoNode = sceneInfo.putObject("h5_info");
        h5InfoNode.put("type", "Wap");

        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            // http repsonse
            HttpEntity entity = response.getEntity();
            String s = EntityUtils.toString(entity);
            log.info("返回参数为:{}", s);

            JSONObject json = JSON.parseObject(s);
            String h5Url = json.getString("h5_url");

            // 拼接参数返回APP
            Map<String, String> result = new HashMap<>();
            result.put("h5_url", h5Url); // 应用appid
            return result;
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new ResultException(Errors.ERROR);
        }
    }

小程序支付

截图

 流程:

        1. 填写[订单号]和[订单金额]和[openId],请求接口

        2.返回客户端[prepay_id],客户端拿到prepay_id就能调起支付

后端参照及接口说明:微信支付-开发者文档

前端参照及接口说明:微信支付-开发者文档

public static Map<String, String> MiniProgram(
            Long orderId, BigDecimal amount, NotifyUrl notifyUrl, String openId) {
        String APPID = APPID_MiniProgram;

        CloseableHttpClient httpClient = buildHttp();
        HttpPost httpPost = buildHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode
                .put("mchid", mchId)
                .put("appid", APPID)
                .put("description", "小程序在线付费")
                .put("notify_url", CURRENT_ENV_URL + notifyUrl.getUrl())
                .put("out_trade_no", orderId.toString());

        amount = (amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP));

        rootNode.putObject("amount").put("total", amount.intValue());
        rootNode.putObject("payer").put("openid", openId);

        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            // http repsonse
            HttpEntity entity = response.getEntity();
            String s = EntityUtils.toString(entity);
            log.info("返回参数为:{}", s);

            JSONObject json = JSON.parseObject(s);
            String prepayId = json.getString("prepay_id");
            String packageStr = "prepay_id=" + prepayId;
            // 拼接参数返回APP
            String nonce = generateNonceStr();
            long timeStamp = System.currentTimeMillis() / 1000;

            Map<String, String> result = new HashMap<>();
            result.put("appId", APPID); // 应用appid
            result.put("timeStamp", Long.toString(timeStamp));
            result.put("nonceStr", nonce);
            result.put("package", packageStr);
            String sign =
                    PrivateKeySign.getToken(
                            APPID, packageStr, nonce, timeStamp, getPrivateKey(privateKeyPath));
            // 签名
            result.put("paySign", sign); // 再次签名
            result.put("signType", "RSA"); // 再次签名

            return result;
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new ResultException(Errors.ERROR);
        }
    }

PC端网页支付

截图

 流程:

        1. 填写[订单号]和[订单金额],请求接口

        2.返回客户端[二维码链接],客户端拿到[二维码链接]后,生成二维码。

        3.用户使用手机微信扫描,即可支付。

后端参照及接口说明:微信支付-开发者文档

前端参照及接口说明:微信支付-开发者文档

代码:

public static Map<String, String> PC(Long orderId, BigDecimal amount, NotifyUrl notifyUrl) {
        CloseableHttpClient httpClient = buildHttp();
        HttpPost httpPost = buildHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        //    65秒后过期
        Date date = DateUtil.getAfterDate(new Date(), Calendar.SECOND, 65);
        String formatDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(date);
        log.info(formatDate);
        rootNode
                .put("mchid", mchId)
                .put("appid", APPID_PC)
                .put("description", "PC端在线付费")
                .put("notify_url", CURRENT_ENV_URL + notifyUrl.getUrl())
                .put("out_trade_no", orderId.toString())
                .put("time_expire", formatDate);

        amount = (amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP));
        rootNode.putObject("amount").put("total", amount.intValue());
        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            // http repsonse
            HttpEntity entity = response.getEntity();
            String s = EntityUtils.toString(entity);
            log.info("返回参数为:{}", s);

            JSONObject json = JSON.parseObject(s);
            String codeUrl = json.getString("code_url");

            // 拼接参数返回APP
            Map<String, String> result = new HashMap<>();
            result.put("codeUrl", codeUrl); // 应用appid
            return result;
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new ResultException(Errors.ERROR);
        }
    }

PayPal(代码已完成,流程待补充)

需要的配置信息:
        #paypay的ClientId和Secret
        pay.config.paypal.key=key
        pay.config.paypal.secret=secret
        #paypalModel 可选参数:[sandbox/production]
        pay.config.paypal.mode=sandbox
        #沙盒 https://api-m.sandbox.paypal.com
        #正式 https://api-m.paypal.com
        pay.config.paypal.base.url=https://api-m.sandbox.paypal.com
        #PAYPAL支付成功后,前端跳转的页面
        pay.config.paypal.return.url=https://www.baidu.com/s?wd=支付成功
        #PAYPAL支付取消后,前端的跳转页面
        pay.config.paypal.cancel.url=https://www.baidu.com/s?wd=支付取消

        如果PayPal需要接订阅(比如按[月/年]自动扣费),还需要创建[商品]和[付款计划]。

PayPal获取Token

代码:

   public String getPaypalToken() {
        String body = HttpRequest.post(PAYPAL_BASE_URL + "/v1/oauth2/token")
                .basicAuth(PAYPAL_KEY, PAYPAL_SECRET)
                .form("grant_type", "client_credentials")
                .execute().body();
        log.info("[paypal支付]-[获取token]->{}", body);
        JSONObject jsonObject = JSONObject.parseObject(body);
        return jsonObject.get("access_token").toString();
    }

订单支付

代码

public Object paypalOrderPay(BigDecimal subtotal, Long orderId) {
        //  接口请参照:https://developer.paypal.com/api/rest/
        /*
         * 支付流程:
         * 1. 创建Payer对象并设置PaymentMethod
         * 2. 设置RedirectUrls并设置cancelURL和returnURL
         * 3. 设置详细信息并添加PaymentDetails
         * 4. 设置金额
         * 5. 设置交易
         * 6. 添加付款明细并将Intent设置为“授权”
         * 7. 通过传递clientID、secret和mode创建APIContext
         * 8. 创建Payment对象并获取paymentID
         * 9. 将payerID 设置为 PaymentExecution 对象
         * 10.执行支付并获得授权
         */
        BigDecimal shipping = new BigDecimal("0.00");
        BigDecimal tax = new BigDecimal("0.00");
        BigDecimal total = shipping.add(subtotal).add(tax);

        Payer payer = new Payer();
        payer.setPaymentMethod("paypal");

        // 重定向网址
        RedirectUrls redirectUrls = new RedirectUrls();
        //取消支付url
        redirectUrls.setCancelUrl(PAYPAL_CANCEL_URL);
        //支付成功后,前端跳转页面
        redirectUrls.setReturnUrl(PAYPAL_RETURN_URL);

        // 设置交易金额  1.运费 2.小计 3.税
        Details details = new Details();
        details.setShipping(shipping.toString());
        details.setSubtotal(subtotal.toString());
        details.setTax(tax.toString());

        // 设置付款金额 total = shipping + subtotal + tax
        Amount amount = new Amount();
        amount.setCurrency("USD");
        amount.setTotal(total.toString());
        amount.setDetails(details);

        // 设置交易信息
        Transaction transaction = new Transaction();
        transaction.setAmount(amount);
        transaction.setDescription("desc");
        transaction.setCustom(orderId.toString());
        List<Transaction> transactions = new ArrayList<>();
        transactions.add(transaction);

        // 添加交易明细
        Payment payment = new Payment();
        //设置付款意图以授权
        payment.setIntent("authorize");
        payment.setPayer(payer);
        payment.setTransactions(transactions);
        payment.setRedirectUrls(redirectUrls);

        //传递 clientID、secret 和 mode。 最简单、使用最广泛的选项。
        APIContext apiContext = new APIContext(PAYPAL_KEY, PAYPAL_SECRET, PAYPAL_MODE);
        try {
            payment = payment.create(apiContext);
            log.info("[paypal支付]-[生成订单]->{}", payment);
        } catch (PayPalRESTException e) {
            log.error("paypal支付异常", e);
            throw new ResultException(Errors.ERROR);
        }
        List<Links> links = payment.getLinks();
        return getRelLink(links, "approval_url").getHref();
    }

创建订阅(订阅支付)

代码:

 public Object createSubscription(String planId, Long orderId) {
        Map<String, String> map = new HashMap<>(4);
        map.put("Content-Type", "application/json");
        map.put("Authorization", getPaypalToken());

        String string = handlerSubsParam(planId, orderId);
        String body = HttpRequest.post(PAYPAL_BASE_URL + "/v1/billing/subscriptions")
                .addHeaders(map)
                .basicAuth(PAYPAL_KEY, PAYPAL_SECRET)
                .body(string)
                .execute().body();
        log.info("[paypal支付]-[创建订阅]->{}", body);
        JSONObject jsonObject = JSONObject.parseObject(body);
        List<Links> links = JSONObject.parseArray(jsonObject.get("links").toString(), Links.class);
        return getRelLink(links, "approve").getHref();
    }

    /**
     * 处理订阅参数
     */
    private String handlerSubsParam(String planId, Long orderId) {
        SubscriptionDTO subscriptionDTO = new SubscriptionDTO();
        subscriptionDTO.setPlanId(planId);

        subscriptionDTO.setCustomId(orderId.toString());
        ApplicationContext applicationContext = new ApplicationContext();
        applicationContext.setCancelUrl(PAYPAL_CANCEL_URL);
        applicationContext.setReturnUrl(PAYPAL_RETURN_URL);
        applicationContext.setPaymentMethod(new PaymentMethod());
        subscriptionDTO.setApplicationContext(applicationContext);

        String string;
        try {
            string = new ObjectMapper().writeValueAsString(subscriptionDTO);
        } catch (Exception e) {
            log.error("创建订阅失败", e);
            throw new ResultException(Errors.ERROR);
        }
        return string;
    }

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值