stripe国际支付(对接支付宝、微信)

前言:stripe国际支付现在网上资料很少,且不支持中国,所以如果要用需要去支持的国家注册商户,官网的java demo是用的spark框架,我这里用的spring,验签需要手动验签,且不能用官网的方法

正文:

支付宝文档地址:https://stripe.com/docs/sources/alipay

微信支付文档地址:https://stripe.com/docs/sources/wechat-pay

支付宝和微信的流程差不多一样。

做了个简单的时序图

关于配置:

回调地址配置,可以每个触发事件都配置不一样的url,也可以都配置一个,我只配置了一个

获得source时候发送请求用到的公匙:

代码:

<!-- stripe支付官方包 -->
<dependency>
    <groupId>com.stripe</groupId>
    <artifactId>stripe-java</artifactId>
    <version>7.26.0</version>
</dependency>

其实只有三个接口:

1.给前端准备数据

2.授权完回调地址

3.回调接口

回调接口:

常量类:

service:

package com.otcbi.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.JsonSyntaxException;
import com.otcbi.coin.common.exception.ApiException;
import com.otcbi.coin.common.exception.ApiResponMessage;
import com.otcbi.constat.Consts;
import com.otcbi.pay.entity.StripeOrder;
import com.otcbi.pay.service.IGoodsOnlineService;
import com.otcbi.pay.service.IStripeOrderService;
import com.otcbi.pay.service.IStripeWebhookMessageService;
import com.otcbi.shop.dto.StripeCreateSourceDTO;
import com.otcbi.shop.entity.Orders;
import com.otcbi.shop.service.IOrdersService;
import com.stripe.Stripe;
import com.stripe.exception.SignatureVerificationException;
import com.stripe.model.Charge;
import com.stripe.model.Event;
import com.stripe.model.EventData;
import com.stripe.net.Webhook;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
public class IGoodsOnlineServiceImpl implements IGoodsOnlineService {

    private static final Logger logger = LoggerFactory.getLogger(IGoodsOnlineServiceImpl.class);

    @Value("${stripe.homeUrl}")
    private String homeUrl;

    @Value("${stripe.apiKey}")
    String stripeApiKey;

    @Value("${stripe.webhookSecret}")
    String stripeWebhookSecret;


    @Autowired
    private IOrdersService iOrdersService;

    @Autowired
    private IStripeOrderService iStripeOrderService;

    @Autowired
    private IStripeWebhookMessageService iStripeWebhookMessageService;

    public StripeCreateSourceDTO createSource(Long ordersId) throws Exception {
        Orders orders = iOrdersService.getById(ordersId);
        if (orders == null) {
            throw new ApiException(ApiResponMessage.SHOP_ORDERS_EXIST, null);
        } else if (orders.getPayState() == 2) {
            throw new ApiException(ApiResponMessage.ORDERS_ALREADY_FINISHED, null);
        }
        StripeCreateSourceDTO source = new StripeCreateSourceDTO();
        source.setUserId(orders.getUserId());

        if (orders.getPayType() == 1) {
            source.setType("alipay");
        } else if (orders.getPayType() == 2) {
            source.setType("wechat");
        }
        source.setCurrency(orders.getCoin());
        source.setOrderNum(orders.getOrderNum());
        source.setReturnUrl(homeUrl + "/stripe/createSourceRetuenBack?orderNum=" + orders.getOrderNum());

        QueryWrapper<StripeOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("is_deleted", "N");
        wrapper.eq("order_num", orders.getOrderNum());
        wrapper.eq("is_cancel", 0);
        StripeOrder stripeOrder = iStripeOrderService.getOne(wrapper);

        if (stripeOrder == null) {
            //创建stripe订单
            stripeOrder = new StripeOrder();
            stripeOrder.setIsDeleted("N");
            stripeOrder.setCreateTime(LocalDateTime.now());
            stripeOrder.setOrderNum(orders.getOrderNum());
            stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_PENDING);
            Integer realAmount = Integer.valueOf(new BigDecimal(orders.getActualPrice().toString()).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_DOWN).toString());
            Integer amount = Integer.valueOf(realAmount.toString());
            source.setAmount(amount);
            stripeOrder.setAmount(realAmount);
            stripeOrder.setType(source.getType());
            stripeOrder.setUserId(orders.getUserId());
            stripeOrder.setCurrency(orders.getCoin());
            iStripeOrderService.save(stripeOrder);
            return source;
        } else {
            String status = stripeOrder.getStatus();

            if (Consts.PAY_STRIPE_STATUS_SUCCEEDED.equals(status)) {
                //已经完成
                throw new ApiException(ApiResponMessage.ORDERS_ALREADY_FINISHED, null);
            } else if (Consts.PAY_STRIPE_STATUS_CANCELED.equals(status) || Consts.PAY_STRIPE_STATUS_INSUFFICIENT_fUNDS.equals(status)
                    || Consts.PAY_STRIPE_STATUS_INVALID_AMOUNT.equals(status) || Consts.PAY_STRIPE_STATUS_CHARGE_ERROR.equals(status)) {
                //已经失效,删除原来订单,重新创建订单
                stripeOrder.setIsCancel(1);
                stripeOrder.setModifyTime(LocalDateTime.now());
                iStripeOrderService.updateById(stripeOrder);

                StripeOrder stripeOrderNew = new StripeOrder();
                stripeOrderNew.setIsDeleted("N");
                stripeOrderNew.setCreateTime(LocalDateTime.now());
                stripeOrderNew.setOrderNum(orders.getOrderNum());
                stripeOrderNew.setStatus(Consts.PAY_STRIPE_STATUS_PENDING);
                Integer realAmount = Integer.valueOf(new BigDecimal(orders.getActualPrice().toString()).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_DOWN).toString());
                Integer amount = Integer.valueOf(realAmount.toString());
                source.setAmount(amount);
                stripeOrderNew.setAmount(realAmount);
                stripeOrderNew.setType(source.getType());
                stripeOrder.setUserId(orders.getUserId());
                stripeOrder.setCurrency(orders.getCoin());
                iStripeOrderService.save(stripeOrderNew);
                return source;
            } else {
                if (Consts.PAY_STRIPE_STATUS_PENDING.equals(status)) {
                    //还未授权
                    Integer amount = Integer.valueOf(new BigDecimal(stripeOrder.getAmount().toString()).setScale(0, BigDecimal.ROUND_DOWN).toString());
                    source.setAmount(amount);
                    return source;
                } else if (Consts.PAY_STRIPE_STATUS_CHARGEABLE.equals(status)) {
                    //已经授权,按理来说这个时候已经完成了订单
                    throw new ApiException(ApiResponMessage.SHOP_ORDERS_PLAYING_ERROR, null);
                } else if (Consts.PAY_STRIPE_STATUS_FAILED.equals(status)) {
                    //拒绝授权支付,重新要求授权
                    Integer amount = Integer.valueOf(new BigDecimal(stripeOrder.getAmount().toString()).setScale(0, BigDecimal.ROUND_DOWN).toString());
                    source.setAmount(amount);
                    return source;
                } else {
                    throw new ApiException(ApiResponMessage.SHOP_GOODS_WEIXIN_PAY_ERROR, null);
                }
            }
        }
    }

    /**
     * 如果客户的支付宝账户被非法使用,支付宝和Stripe会在内部处理此问题。在支付宝的情况下,
     * 只有当客户对所提供的商品或服务有投诉时,付款才会有争议。
     * 如果发生争议,dispute.created则会发送webhook事件,Stripe会从您的Stripe余额中扣除争议金额。
     * <p>
     * 一个chargeable单一使用支付宝源必须在6小时内变得收费chargeable。如果不是,则其状态将自动转换为canceled
     * 您的集成并收到source.canceledwebhook事件。取消收费来源后,客户的授权支付宝付款将自动退还 -
     * 不会将任何款项转入您的帐户。因此,请确保您的订单已取消,并在收到source.canceled活动时通知客户。
     * 此外,pending如果不用于授权付款,则在一小时后取消资源,确保所有资源最终从pending状态转移到canceled状态(如果不使用)。
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @Transactional
    public synchronized void stripeCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("============进入webhook回调=================");
        //验签
        Event event = attestation(request, response);
        if (event == null) {
            return;
        }

        /*不验签获得数据
        Event event = ApiResource.GSON.fromJson(request.getReader(), Event.class);*/
        String message = event.toJson();
        logger.info("收到内容为:" + message);
        String stripeType = event.getType();

        //把信息记录到表
        iStripeWebhookMessageService.saveWebHootMssage(event, message, stripeType);

        if (stripeType.equals("source." + Consts.PAY_STRIPE_STATUS_CHARGEABLE)) {
            //用户授权确认
            callBackPending(event, response);
        } else if (stripeType.equals("source." + Consts.PAY_STRIPE_STATUS_FAILED)) {
            //用户拒绝授权
            callBackFailed(event, response);
        } else if (stripeType.equals("source." + Consts.PAY_STRIPE_STATUS_CANCELED)) {
            //过期
            callBackCanceled(event, response);
        } else if (stripeType.equals("charge." + Consts.PAY_STRIPE_STATUS_SUCCEEDED)) {
            //stripe收款成功通知
            callBackSuccess(event, response);
        } else if (stripeType.equals("charge.dispute." + Consts.PAY_STRIPE_STATUS_CREATED)) {
            //客户投诉退款
            callBackCreated(event, response);
        } else {
            logger.error("获得未知状态" + stripeType);
            response.setStatus(500);
            logger.info("============webhook回调结束=================");
            return;
        }
        response.setStatus(200);
        logger.info("============webhook回调结束=================");
    }

    public Event attestation(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String endpointSecret = stripeWebhookSecret;
        InputStream inputStream = request.getInputStream();
        byte[] bytes = IOUtils.toByteArray(inputStream);
        String payload = new String(bytes, "UTF-8");

        String sigHeader = request.getHeader("Stripe-Signature");
        Event event = null;
        try {
            event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
            logger.info("验签成功");
            response.setStatus(200);
            return event;
        } catch (JsonSyntaxException e) {
            logger.error("验签失败");
            response.setStatus(400);
        } catch (SignatureVerificationException e) {
            logger.error("验签失败");
            e.printStackTrace();
            response.setStatus(400);
        }
        logger.info("============webhook回调结束=================");
        return null;
    }

    public void callBackPending(Event event, HttpServletResponse response) throws Exception {
        EventData eventData = event.getData();
        JSONObject json = JSONObject.parseObject(eventData.getObject().toJson());
        String client_secret = json.getString("client_secret");
        String orderNum = json.getString("statement_descriptor");
        String stripe_id = json.getString("id");
        String type = json.getString("type");
        String amount = json.getString("amount");
        String currency = json.getString("currency");
        String userId = json.getJSONObject("owner").getString("name");

        QueryWrapper<StripeOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("is_deleted", "N");
        wrapper.eq("order_num", orderNum);
        wrapper.eq("is_cancel", 0);
        wrapper.eq("type", type);
        wrapper.eq("user_id", userId);
        StripeOrder stripeOrder = iStripeOrderService.getOne(wrapper);
        if (stripeOrder == null) {
            //订单不存在
            logger.error("Stripe收到不存在的订单回调" + orderNum);
            response.setStatus(500);
        } else {
            if (!stripeOrder.getStatus().equals(Consts.PAY_STRIPE_STATUS_PENDING)) {
                logger.error("Stripe收到违法订单" + orderNum);
            }
            if (!amount.equals(stripeOrder.getAmount().toString())) {
                logger.error("Stripe收到金额对不上的订单回调" + orderNum);
                response.setStatus(500);
            }
            //客户已经完成授权
            stripeOrder.setStripeId(stripe_id);
            stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_CHARGEABLE);
            stripeOrder.setClientSecret(client_secret);
            stripeOrder.setModifyTime(LocalDateTime.now());
            iStripeOrderService.updateById(stripeOrder);
            logger.info("===================" + orderNum + "订单授权成功=============");
            //创建支付订单
            try {
                createCharge(stripeOrder, Integer.valueOf(amount), currency, stripe_id, orderNum);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error(orderNum + "stripe创建收费订单失败");
            }

        }
    }

    /**
     * 拒绝授权
     *
     * @param event
     * @param response
     * @throws Exception
     */
    public void callBackFailed(Event event, HttpServletResponse response) throws Exception {
        EventData eventData = event.getData();
        JSONObject json = JSONObject.parseObject(eventData.getObject().toJson());
        String client_secret = json.getString("client_secret");
        String orderNum = json.getString("statement_descriptor");
        String stripe_id = json.getString("id");
        String type = json.getString("type");
        String amount = json.getString("amount");
        String currency = json.getString("currency");
        String userId = json.getJSONObject("owner").getString("name");

        QueryWrapper<StripeOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("is_deleted", "N");
        wrapper.eq("order_num", orderNum);
        wrapper.eq("is_cancel", 0);
        wrapper.eq("type", type);
        wrapper.eq("user_id", userId);
        StripeOrder stripeOrder = iStripeOrderService.getOne(wrapper);
        if (stripeOrder == null) {
            //订单不存在
            logger.error("Stripe收到不存在的订单回调" + orderNum);
            response.setStatus(500);
        } else {
            if (!stripeOrder.getStatus().equals(Consts.PAY_STRIPE_STATUS_PENDING)) {
                logger.error("Stripe收到违法订单" + orderNum);
            }
            if (!amount.equals(stripeOrder.getAmount().toString())) {
                logger.error("Stripe收到金额对不上的订单回调" + orderNum);
                response.setStatus(500);
            }
            //客户不授权删除订单
            stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_FAILED);
            stripeOrder.setClientSecret(client_secret);
            stripeOrder.setModifyTime(LocalDateTime.now());
            stripeOrder.setIsCancel(1);
            stripeOrder.setStripeId(stripe_id);
            iStripeOrderService.updateById(stripeOrder);
            logger.info("===================" + orderNum + "订单拒绝授权处理成功=============");
        }
    }


    /**
     * 过期订单
     *
     * @param event
     * @param response
     * @throws Exception
     */
    public void callBackCanceled(Event event, HttpServletResponse response) throws Exception {
        EventData eventData = event.getData();
        JSONObject json = JSONObject.parseObject(eventData.getObject().toJson());
        String client_secret = json.getString("client_secret");
        String orderNum = json.getString("statement_descriptor");
        String stripe_id = json.getString("id");
        String type = json.getString("type");
        String amount = json.getString("amount");
        String currency = json.getString("currency");
        String userId = json.getJSONObject("owner").getString("name");

        QueryWrapper<StripeOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("is_deleted", "N");
        wrapper.eq("order_num", orderNum);
        wrapper.eq("is_cancel", 0);
        wrapper.eq("type", type);
        wrapper.eq("user_id", userId);
        wrapper.eq("stripe_id", stripe_id);
        StripeOrder stripeOrder = iStripeOrderService.getOne(wrapper);
        if (stripeOrder == null) {
            //订单不存在
            logger.error("Stripe收到不存在的订单回调" + orderNum);
            response.setStatus(500);
        } else {
            if (!stripeOrder.getStatus().equals(Consts.PAY_STRIPE_STATUS_PENDING)) {
                logger.error("Stripe收到违法订单" + orderNum);
            }
            if (!amount.equals(stripeOrder.getAmount().toString())) {
                logger.error("Stripe收到金额对不上的订单回调" + orderNum);
                response.setStatus(500);
            }
            //订单过期删除订单
            stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_CANCELED);
            stripeOrder.setClientSecret(client_secret);
            stripeOrder.setModifyTime(LocalDateTime.now());
            stripeOrder.setIsCancel(1);
            iStripeOrderService.updateById(stripeOrder);
            logger.info("===================" + orderNum + "订单过期处理成功=============");
        }
    }

    public void callBackCreated(Event event, HttpServletResponse response) throws Exception {
        EventData eventData = event.getData();
        //待写
    }


    public void callBackSuccess(Event event, HttpServletResponse response) throws Exception {
        EventData eventData = event.getData();
        JSONObject json = JSONObject.parseObject(eventData.getObject().toJson());
        String orderNum = json.getJSONObject("source").getString("statement_descriptor");
        String stripe_id = json.getJSONObject("source").getString("id");
        String type = json.getJSONObject("source").getString("type");
        String amount = json.getJSONObject("source").getString("amount");
        String userId = json.getJSONObject("source").getJSONObject("owner").getString("name");
        String payId = json.getString("id");

        QueryWrapper<StripeOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("is_deleted", "N");
        wrapper.eq("order_num", orderNum);
        wrapper.eq("is_cancel", 0);
        wrapper.eq("type", type);
        wrapper.eq("user_id", userId);
        wrapper.eq("stripe_id", stripe_id);
        StripeOrder stripeOrder = iStripeOrderService.getOne(wrapper);
        if (stripeOrder == null) {
            //订单不存在
            logger.error("Stripe收到不存在的订单回调" + orderNum);
            response.setStatus(500);
        } else {
            if (!stripeOrder.getStatus().equals(Consts.PAY_STRIPE_STATUS_CHARGEABLE)) {
                logger.error("Stripe收到违法订单" + orderNum);
            }
            if (!amount.equals(stripeOrder.getAmount().toString())) {
                logger.error("Stripe收到金额对不上的订单回调" + orderNum);
                response.setStatus(500);
            }
            //客户付款成功
            stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_SUCCEEDED);
            stripeOrder.setModifyTime(LocalDateTime.now());
            stripeOrder.setPayId(payId);
            iStripeOrderService.updateById(stripeOrder);
            //更改订单状态
            QueryWrapper<Orders> orderWrapper = new QueryWrapper<>();
            orderWrapper.eq("order_num", orderNum);
            orderWrapper.eq("is_deleted", "N");
            Orders orders = iOrdersService.getOne(orderWrapper);
            orders.setModifyTime(LocalDateTime.now());
            orders.setPayState(2);
            iOrdersService.updateById(orders);
            logger.info("===================" + orderNum + "订单付款成功=============");
        }
    }

    /**
     * 创建收费订单
     *
     * @param amount
     * @param currency
     * @param stripeId
     * @throws Exception 支付宝付款的费用创建可能会返回以下任何错误:
     *                   insufficient_funds    支付宝账户资金不足,无法完成购买。客户应为其帐户注资并重试,或使用其他付款方式。
     *                   invalid_amount    如果收费金额大于支付宝支持的费用,则会发生这种情况。
     */
    public void createCharge(StripeOrder stripeOrder, Integer amount, String currency, String stripeId, String orderNum) throws Exception {
        Stripe.apiKey = stripeApiKey;
        Map<String, Object> chargeParams = new HashMap<String, Object>();
        chargeParams.put("amount", amount);
        chargeParams.put("currency", currency);
        chargeParams.put("source", stripeId);
        chargeParams.put("description", orderNum + " completion of payment");

        Charge charge = Charge.create(chargeParams);
        String errorMsg = charge.getFailureMessage();
        if (StringUtils.isEmpty(errorMsg)) {
            logger.info(orderNum + "订单请求支付成功");
        } else {
            if (errorMsg.equals("insufficient_funds")) {
                logger.error(orderNum + "订单用户支付宝余额不足");
                stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_INSUFFICIENT_fUNDS);
                stripeOrder.setModifyTime(LocalDateTime.now());
            } else if (errorMsg.equals("invalid_amount")) {
                logger.error(orderNum + "订单收费金额大于支付宝支持的费用");
                stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_INVALID_AMOUNT);
                stripeOrder.setModifyTime(LocalDateTime.now());
            } else {
                logger.error(orderNum + errorMsg);
                stripeOrder.setStatus(Consts.PAY_STRIPE_STATUS_CHARGE_ERROR);
                stripeOrder.setModifyTime(LocalDateTime.now());
            }
            iStripeOrderService.updateById(stripeOrder);
        }
        logger.info("===================" + orderNum + "订单申请扣费成功=============");
    }


}

h5页面:

createSource:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--官方js-->
    <script src="https://js.stripe.com/v3/"></script>
    <script type="text/javascript" src="/jquery.min.js"></script>

    <script type="text/javascript">
        $(document).ready(function () {
            var amount = $("#amount").val();
            var returnUrl = $("#returnUrl").val();
            var currency = $("#currency").val();
            var type = $("#type").val();
            var userId = $("#userId").val();
            var orderNum = $("#orderNum").val();

            var stripe = Stripe($('#stripe').val());
            stripe.createSource({
                type: type,
                amount: amount,
                currency: currency,
                statement_descriptor : orderNum,
                owner: {
                    name: userId,
                },
                redirect: {
                    return_url: returnUrl
                },
            }).then(function (result) {
                // handle result.error or result.source
                if (result.source != null) {
                    if(result.source.type == "wechat"){
                        window.location.href=result.source.wechat.qr_code_url;
                    }else {
                        window.location.href=result.source.redirect.url;
                    }
                } else {
                    alert(result.error.message);
                }
            });
        });
    </script>
</head>

</head>
<body>
<h1>跳转授权页面中。。。。</h1>
<input type="hidden" id="stripe" th:value="${stripe}">
<input type="hidden"  id="type" th:value="${dto.type}">
<input type="hidden"  id="amount" th:value="${dto.amount}">
<input type="hidden"  id="returnUrl" th:value="${dto.returnUrl}">
<input type="hidden"  id="currency" th:value="${dto.currency}">
<input type="hidden"  id="userId" th:value="${dto.userId}">
<input type="hidden"  id="orderNum" th:value="${dto.orderNum}">
</body>
</html>

temp.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="/jquery.min.js"></script>
</head>
<body>
        <h1>您的订单授权已受理,正在等待第三方处理,请稍后查询订单状态</h1>
</body>
</html>

这里遇到一个特别坑的地方:就是验签的时候官网给的demo是:

用到spark,以为引入spark pom就行了,结果一直不行,后来联系他们客服,然后给了我个irc的地址让我跟他们技术人员谈,聊了好久才知道,原来他们用的是spark框架不能和spring拼凑使用,聊天内容:

聊天知道了几个重要内容:

如果不响应会持续回调72小时,时间递增的回调

验签获取payload 的获取关键代码:

InputStream inputStream = request.getInputStream();
byte[] bytes = IOUtils.toByteArray(inputStream);
String payload = new String(bytes, "UTF-8");

如果有什么不懂欢迎留言!

猜你喜欢

转载自blog.csdn.net/Java_Mrsun/article/details/88393871