支付宝支付

简介: 支付宝支付

想对接支付,就需要申请对应的支付渠道。否则你只能模拟一个支付和支付成功,来走完自己的流程。

目前国内主要有微信支付和支付宝支付两种主流支付方式,但是微信支付不支持个体户,因此这里选择支付宝的沙箱支付。

其实支付流程基本如下图所示:


现在可以先去支付宝开发平台申请资质,https://open.alipay.com/develop/manage

然后直接下载手机版沙箱支付宝。

实现方案

以下是测试用例(java版):

public class PayTest {
 
    // 「沙箱环境」应用ID - 您的APPID,收款账号既是你的APPID对应支付宝账号。获取地址;https://open.alipay.com/develop/sandbox/app
    public static String app_id = "你的APPID";
    // 「沙箱环境」商户私钥,你的PKCS8格式RSA2私钥
    public static String app_private_key = "你的商户私钥"
    // 「沙箱环境」支付宝公钥
    public static String alipay_public_key = "你的支付宝公钥"
    // 「沙箱环境」服务器异步通知页面路径。如果非公网地址建议使用 natapp 等内网穿透工具
    public static String notify_url = "异步支付结果通知地址";
    // 「沙箱环境」页面跳转同步通知页面路径 需http://格式的完整路径,必须外网可以正常访问,才会同步跳转
    public static String return_url = "同步页面回调地址,可自动返回商户页面";
    // 「沙箱环境」
    public static String gatewayUrl = "支付宝网关地址";
    // 签名方式
    public static String sign_type = "RSA2";
    // 字符编码格式
    public static String charset = "utf-8";
 
    @Test
    public void test_AliPay() throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,
                app_id,
                app_private_key,
                "json",
                charset,
                alipay_public_key,
                sign_type);
 
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();  // 发送请求的 Request类
        request.setNotifyUrl(notify_url);
//        request.setReturnUrl(return_url);
 
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", "daniel82AAAA000032333361X02");  // 我们自己生成的订单编号
        bizContent.put("total_amount", "0.01"); // 订单的总金额
        bizContent.put("subject", "测试商品");   // 支付的名称
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");  // 固定配置
        request.setBizContent(bizContent.toString());
 
        String form = alipayClient.pageExecute(request).getBody();
        log.info("测试结果:{}", form);
    }
}
  1. 这是支付宝沙箱支付 SDK 支付宝扫码的调用案例,本身这个 SDK 支持了支付宝所有的支付方式,非常方便。
  2. 支付宝沙箱不影响实际环境,且容易申请,如果可能,可以申请微信支付、
  3. 案例运行成功后,会拿到一个html表单,这是一个动态表单,直接加载html就可以去往支付界面。

具体实现

创建订单

@RequestMapping(value = "create_pay_order", method = RequestMethod.POST)
    public Response<String> createParOrder(@RequestHeader("Authorization") String token, @RequestParam Integer productId) {
        try {
            // 1. 校验token
            boolean success = authService.checkToken(token);
            if (!success) {
                return Response.<String>builder()
                        .code(Constants.ResponseCode.TOKEN_ERROR.getCode())
                        .info(Constants.ResponseCode.TOKEN_ERROR.getInfo())
                        .build();
            }
            // 2. token解析
            String openId = authService.getOpenId(token);
            assert openId != null;
            log.info("用户商品下单,根据商品ID创建支付单开始 openid:{} productId:{}", openId, productId);
 
            ShopCartEntity shopCartEntity = ShopCartEntity.builder()
                    .openid(openId)
                    .productId(productId)
                    .build();
 
            PayOrderEntity orderEntity = orderService.createOrder(shopCartEntity);
            log.info("用户商品下单,根据商品ID创建支付单完成 openid: {} productId: {} orderPay: {}", openId, productId, orderEntity.toString());
 
            return Response.<String>builder()
                    .code(Constants.ResponseCode.SUCCESS.getCode())
                    .info(Constants.ResponseCode.SUCCESS.getInfo())
                    .data(orderEntity.getPayUrl())
                    .build();
 
 
        }catch (Exception e) {
            log.error("用户商品下单,根据商品ID创建支付单失败", e);
            return Response.<String>builder()
                    .code(Constants.ResponseCode.UN_ERROR.getCode())
                    .info(Constants.ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }


public interface IOrderService {
 
    /**
     * 用户下单,通过购物车信息,返回下单后的支付单
     *
     * @param shopCartEntity 简单购物车
     * @return 支付单实体对象
     */
    PayOrderEntity createOrder(ShopCartEntity shopCartEntity);
    
    //... 省略其他方法
    
}    
 
 
 
 @Override
    public PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception {
        String openid = shopCartEntity.getOpenid();
 
        // 1. 查询有效的未支付订单
        UnpaidOrderEntity unpaidOrderEntity = orderRepository.queryUnpaidOrder(shopCartEntity);
        if (unpaidOrderEntity != null && unpaidOrderEntity.getPayStatus().equals(PayStatusVO.WAIT)) {
            log.info("创建订单-存在,已存在未支付订单,返回 openid: {} orderId: {} payUrl: {}", openid, unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getPayUrl());
            return PayOrderEntity.builder()
                    .openid(openid)
                    .payStatus(unpaidOrderEntity.getPayStatus())
                    .payUrl(unpaidOrderEntity.getPayUrl())
                    .orderId(unpaidOrderEntity.getOrderId())
                    .build();
        }else if (unpaidOrderEntity != null && unpaidOrderEntity.getPayStatus().equals(PayStatusVO.CREATE)) {
            log.info("创建订单-存在,存在未创建支付单订单,返回 openid: {} orderId: {}", openid, unpaidOrderEntity.getOrderId());
            PayOrderEntity payOrderEntity = this.doPrepayOrder(openid, unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getProductName(), unpaidOrderEntity.getTotalAmount());
            log.info("创建订单-完成,生成支付单。openid: {} orderId: {} payUrl: {}", openid, payOrderEntity.getOrderId(), payOrderEntity.getPayUrl());
            return payOrderEntity;
        }
 
        // 2. 查询商品
        ProductEntity productEntity = orderRepository.queryProduct(shopCartEntity.getProductId());
        // 如果所购商品已下线
        if (!productEntity.isAvailable()) {
            throw new ChatGPTException(Constants.ResponseCode.ORDER_PRODUCT_ERR.getCode(), Constants.ResponseCode.ORDER_PRODUCT_ERR.getInfo());
        }
 
        // 3. 保存订单
        OrderEntity orderEntity = this.doSaveOrder(openid, productEntity);
 
        // 4. 创建支付
        PayOrderEntity payOrderEntity = this.doPrepayOrder(openid, orderEntity.getOrderId(), productEntity.getProductName(), orderEntity.getTotalAmount());
        log.info("创建订单-完成,生成支付单。openid: {} orderId: {} payUrl: {}", openid, orderEntity.getOrderId(), payOrderEntity.getPayUrl());
 
        return payOrderEntity;
    }

创建订单前,要查询有效的未支付订单,如果存在直接返回支付宝支付界面。避免创建一堆的订单。

此外我们做流程分析时候知道,还有可能是订单存在,但无支付单。那么这个时候需要主动创建一条支付单,再返回。

如果确实需要创建新订单,则需要根据购物车商品ID,查询出对应的商品信息,创建并保存订单,最后再创建支付单更新到订单上。

支付回调

@PostMapping("pay_notify")
    public void payNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            log.info("支付回调,消息接收 {}", request.getParameter("trade_status"));
            if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
                Map<String, String> params = new HashMap<>();
                Map<String, String[]> requestParams = request.getParameterMap();
                for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
                    String name = (String) iter.next();
                    String[] values = (String[]) requestParams.get(name);
                    String valueStr = "";
                    for (int i = 0; i < values.length; i++) {
                        valueStr = (i == values.length - 1) ? valueStr + values[i]
                                : valueStr + values[i] + ",";
                    }
                    params.put(name, valueStr);
                }
                String tradeNo = params.get("out_trade_no");
                String gmtPayment = params.get("gmt_payment");
                String alipayTradeNo = params.get("trade_no");
                String sign = params.get("sign");
                Double total_amount = Double.valueOf(params.get("total_amount"));
                boolean checkSignature  = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
 
                // 支付宝验签通过
                if (checkSignature) {
                    // 验签通过
                    log.info("支付回调,交易名称: {}", params.get("subject"));
                    log.info("支付回调,交易状态: {}", params.get("trade_status"));
                    log.info("支付回调,支付宝交易凭证号: {}", params.get("trade_no"));
                    log.info("支付回调,商户订单号: {}", params.get("out_trade_no"));
                    log.info("支付回调,交易金额: {}", params.get("total_amount"));
                    log.info("支付回调,买家在支付宝唯一id: {}", params.get("buyer_id"));
                    log.info("支付回调,买家付款时间: {}", params.get("gmt_payment"));
                    log.info("支付回调,买家付款金额: {}", params.get("buyer_pay_amount"));
                    log.info("支付回调,支付回调,更新订单 {}", tradeNo);
                    // 更新订单为已支付
                    boolean isSuccess =  orderService.changeOrderPaySuccess(tradeNo, alipayTradeNo, BigDecimal.valueOf(total_amount), dateFormat.parse(gmtPayment));
 
                    if (isSuccess) {
                        // 发布消息
//                        eventBus.post(tradeNo);
                        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT,"", tradeNo);
 
                    }
                    response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
                }else {
                    response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
                }
            }
        }catch (Exception e) {
            log.error("支付失败", e);
            response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
        }
    }

支付回调主要以支付宝支付的消息通知为主,更新订单并发送消息。这两步操作都是非常快的,不会让支付回调超时。

所以一般我们不是通过支付回调直接发货,因为发货流程更长,MQ消息解耦则是更好的方式。

注意 eventBus 是 Guava 的消息总线,类似于 MQ 消息。为了应对高并发已经改为了rabbitmq,如果业务简单可以继续用Guava

到这支付功能基本实现了,不过实际的商品下单支付场景肯定还会复杂,并且还得考虑异常,比如下单未支付,支付未发货,消息通知异常等等。小伙伴们可以思考哦。  


目录
相关文章
|
XML 移动开发 API
微信支付开发(7) H5支付
关键字:微信支付 微信支付v3 H5支付 wap支付 prepay_id 作者:方倍工作室原文: http://www.cnblogs.com/txw1958/p/wxpayv3_h5.html    本文介绍微信支付下的H5支付实现流程。
3408 1
|
移动开发 安全 前端开发
〔支付接入〕微信的 h5 支付和 jsapi 支付
学会微信支付,打开你的财富之门
307 2
〔支付接入〕微信的 h5 支付和 jsapi 支付
|
Java 数据安全/隐私保护
支付宝支付功能使用
支付宝支付功能使用
|
缓存 Java 数据安全/隐私保护
支付宝支付
支付宝支付
|
前端开发 区块链 Python
从 0 到 1 看支付
人类社会自从有了分工,就有了交换,而有了交换,也就有了支付,交换可以说是支付最古老的表现形态
254 0
从 0 到 1 看支付
|
机器学习/深度学习
关于支付宝/微信免签实现个人支付
最近一直在了解关于个人支付的问题。由于之前一直想实现个人支付,但是目前微信和支付宝的支付接口都需要企业或个体户资质,导致没办法实现,无奈只能走向这个道路。 说是免签,实际上就是拿到收款金额来做些事情。
关于支付宝/微信免签实现个人支付
|
安全
大话支付
做了半年的支付产品经理,希望通过幽默、白话的方式,对国内线下支付、线上支付的发展有一个简单的概括。有不准确之处,请大家批评指正。
2051 0
|
JavaScript Java 开发工具
【支付宝支付】手机网页内 支付宝支付实现过程
支付宝支付,相比较微信支付,封装了提供给开发者使用的接口,开发者可以在蚂蚁金服开放平台先把开发DEMO下载下来 手机网站内支付    下载地址:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.tFuJYY&treeId=54&articleId=106682&docType=1   在下载的DEMO中,可以找到需要调用的支付宝封装的架包 请自行引入到自己的项目中。
2929 0