Java - 扫呗支付

一、依赖

<dependency>
   <groupId>io.gitee.hwcgetit</groupId>
   <artifactId>saobei-open-sdk</artifactId>
   <version>1.1.8</version>
</dependency>

二、入参(dto)

package com.saobei.entity.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @author yfl
 * @date 2023/02/10
 * <p>
 * * *  小程序支付 入参
 */
@Data
@Accessors(chain = true)
public class SaoBeiMiniPayDto implements Serializable {
    /**
     * 支付方式: 010-微信、020-支付宝、060-qq钱包、080-京东钱包、090-口碑、100-翼支付、110-银联二维码、140-和包支付(仅限和包通道)、000-自动识别类型
     */
    @NotNull(message = "支付方式不能为空")
    private String payType;

    /**
     * 收银机、POS机等设备的公网IP(IPV4格式 ,人行侧风控主要依据,请真实填写)
     */
    private String terminalIp;

    /**
     * 商户侧门店或者终端编号,请先与运营同事确认是否在扫呗平台配置过映射,若没有配置映射,请勿传值,否则支付会报错。
     */
    private String deviceNo;

    /**
     * 终端流水号,填写商户系统的支付订单号,不可重复
     */
    @NotNull(message = "订单号不能为空")
    private String terminalTrace;

    /**
     * 终端交易时间,yyyyMMddHHmmss,全局统一时间格式
     */
    @JsonFormat(pattern = "yyyyMMddHHmmss")
    @DateTimeFormat(pattern = "yyyyMMddHHmmss")
    @NotNull(message = "交易时间不能为空")
    private String terminalTime;

    /**
     * 金额,单位分
     */
    @NotNull(message = "金额不能为空 (单位:分)")
    private String totalFee;

    /**
     * 微信侧商户公众号appid
     */
    private String subAppId;

    /**
     * 用户标识(微信openid,支付宝userid),pay_type为010及020时必填
     */
    private String openid;

    /**
     * 订单描述,显示在订单详情页面,禁止使用+,空格,/,?,%,#,&,=这几类特殊符号
     */
    private String orderBody;

    /**
     * 外部系统通知地址
     */
    private String notifyUrl;

    /**
     * 附加数据,原样返回
     */
    private String attach;

    /**
     * 订单包含的商品列表信息,Json格式。
     * [{"goods_id":"test001","goods_name":"技术单品1","price":"150","quantity":"2"},{"goods_id":"test002","goods_name":"技术单品2","price":"150","quantity":"2"}]
     */
    private String goodsDetail;

    /**
     * 微信侧订单优惠标记,代金券或立减优惠功能的参数(字段值:cs和bld)
     */
    private String goodsTag;

    /**
     * 优惠券串码
     */
    private String couponNo;

    /**
     * 优惠券凭证
     */
    private String couponCredential;

    /**
     * 微信门店编号和支付宝外部自定义门店编号,透传。微信对应scene_info(场景信息)中的门店id。 支付宝自定义门店编号不能随便传,在确认门店编号存在的情况下传值,否则影响支付
     */
    private String customStoreId;

    /**
     * 支付宝官方门店编号
     */
    private String officialStoreId;

    /**
     * 点餐场景类型: qr_order(店内扫码点餐)、pre_order(预点到店自提)、home_delivery (外送到家)、direct_payment(直接付款)、other(其他)
     */
    private String foodOrderType;

    /**
     * 商户号
     */
    private String merchantNo;

    /**
     * 终端号
     */
    private String terminalId;

    /**
     * 支付密钥
     */
    private String accessToken;

    /**
     * 签名
     */
    private String keySign;
}

三、支付常量

package com.saobei.entity.constants;

/**
 * @author yfl
 * @date 2023/02/09
 * <p>
 * * * 扫呗常量
 */
public interface SaoBeiConstants {
    /**
     * 版本号 (当前版本:202) 根据调用的SDK版本来决定版本号
     */
    String PAY_VER = "202";

    /**
     * 机构秘钥
     */
    String KEY = "your key";

    /**
     * 支付秘钥
     */
    String ACCESS_TOKEN = "your token";

    /**
     * 机构号
     */
    String INST_NO = "your inst_no";

    /**
     * 商户号
     */
    String MERCHANT_NO = "your metchant_no";

    /**
     * 终端号
     */
    String TERMINAL_ID = "your_id";
}

       根据业务需求来定义常量,考虑有多个商户的情况下可以将支付的关键信息动态处理。单个商户的话可以内置,但是要注意代码安全哦。

四、枚举

package com.saobei.entity.enums;

import lombok.Getter;

/**
 * @author yfl
 * @date 2023/02/09
 * <p>
 * * * 扫呗请求接口类型枚举类
 */
@Getter
public enum ServerIdEnum {

    ONE("010"),
    TWO("011"),
    THREE("012"),
    FOUR("015"),
    FIVE("020"),
    SIX("030"),
    SEVEN("040"),
    EIGHT("041"),
    NINE("031"),
    TEN("016");

    /*
     * 接口类型: 
     * 付款码支付-010、扫码支付-011、公众号预支付-012、小程序支付接口-015、支付查询-020
     * 退款申请-030、撤销交易-040、关闭订单-041、退款订单查询-031、聚合码支付-016
     */
    private final String type;

    ServerIdEnum(String type) {
        this.type = type;
    }
}

五、Controller层

package com.saobei.controller;

import com.alibaba.fastjson.JSON;
import com.balledu.common.core.domain.ResultData;
import com.balledu.common.core.utils.QrcodeUtil;
import com.saobei.entity.dto.*;
import com.saobei.entity.enums.ServerIdEnum;
import com.saobei.open.sdk.DefaultSaobeiApiClient;
import com.saobei.open.sdk.SaobeiApiClient;
import com.saobei.open.sdk.model.requst.trade.*;
import com.saobei.open.sdk.model.response.trade.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.saobei.entity.constants.SaoBeiConstants.PAY_VER;

/**
 * @author yfl
 * @date 2023/02/09
 * <p>
 * 扫呗支付
 */
@RestController
@RequestMapping("/saobei")
@Slf4j
public class SaoBeiController {

    /**
     * 小程序支付
     * ********
     *
     * @param dto 入参
     * @return 返回结果
     */
    @PostMapping("/minipay")
    public ResultData minipay(@Validated @RequestBody SaoBeiMiniPayDto dto) {

        try {
            SaobeiApiClient<SaobeiMiniPayRequest, SaobeiMiniPayResponse> client = new DefaultSaobeiApiClient<>(dto.getAccessToken());

            SaobeiMiniPayRequest request = new SaobeiMiniPayRequest();
            request.setPay_ver(PAY_VER);
            request.setPay_type(dto.getPayType());
            request.setService_id(ServerIdEnum.FOUR.getType());
            request.setMerchant_no(dto.getMerchantNo());
            request.setTerminal_id(dto.getTerminalId());
            request.setTerminal_trace(dto.getTerminalTrace());
            request.setTerminal_time(dto.getTerminalTime());
            request.setOpen_id(dto.getOpenid());
            request.setSub_appid(dto.getSubAppId());
            request.setTotal_fee(dto.getTotalFee());
            request.setOrder_body(dto.getOrderBody() == null ? "场馆通订单" : dto.getOrderBody());

            log.info("请求报文: {}" + JSON.toJSONString(request));
            SaobeiMiniPayResponse response = client.execute(request);

            log.info("返回报文: {}" + JSON.toJSONString(response));

            return ResultData.createSuccessJsonResult(response);
        } catch (Exception e) {
            log.error("调用遭遇异常,原因:{}" + e.getMessage() + e);

            return ResultData.createFailJsonResult("调用遭遇异常,原因:" + e.getMessage() + e);
        }
    }
}

        Java内部调用扫呗SDK不用自己去验签签名(keySign),其他语言可能需要此操作。

注意:
        ● 该接口为预支付接口,还未进行支付,故该接口不返回优惠字段。优惠字段会在支付完后,通过回调接口返回,或者通过查询接口获取。
        ● 请求完这个接口得到的返回参数直接去调微信小程序/支付宝小程序的js支付(参见下方描述),参数值不用做变化

获取用户标识及调起js支付文档:

1、微信小程序
      ● 获取openid: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
      ● 调js支付: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5

2、支付宝小程序
      ● 获取userid: https://opendocs.alipay.com/mini/05dxgc?pathHash=1a3ecb13
      ● 调js支付: https://opendocs.alipay.com/mini/api/openapi-pay

六、返回结果(vo)

参数名称类型长度必填说明
return_codeString2Y响应码: 01 成功,62 失败,响应码仅代表通信状态,不代表业务结果
return _msgString128Y返回信息提示,“预支付请求成功”“预支付请求失败”等
key _signString32Y签名串
result_codeString2N业务结果:01 成功 02 失败
pay_typeString3N支付方式:010 微信 020 支付宝
merchant_nameString40N商户名称
merchant_noString15N商户号
terminal_idString8N终端号
device_noString32N商户终端设备号(商户自定义,如门店编号).必须在平台已配苦过
terminal_traceString32N终端流水号,商户系统的订单号,系统原样返回
terminal_timeString14N终端交易时间,yyyyMMddHHmmss,全局统一时间格式,系统原样返回
total_feeString12N金额,单位分
out_trade_noString32N平台唯一订单号
appldString16N微信小程序支付返回字段,公众号id
time_stampString32N微信小程序支付返回字段,时间戳,示例: 1414561699标准北京时间,时区为东八区,自1970年1月1日 0点0分(秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
nonce_strString32N微信小程序支付返回字段,随机字符串
package _strString128N微信小程序支付返回字段,订单详情扩展字符串。
sign_typeString32N微信小程序支付返回字段,签名方式,示例: MD5.RSA
pay_signString512N微信小程序支付返回字段,签名
ali_trade_noString32N支付宝小程序支付返回字段用于调起支付宝小程序

● 注意:
● 请求完这个接口得到的返回参数直接去调微信小程序/支付宝小程序,参数值不用做变化。

1)先判断协议字段(return_code)返回,再判断业务(result_code)返回,最后判断交易状态。

2)小程序和公众号支付时,当return_code和result_code两个值都返回01时,才能取值去请求微信支付。否则会由于取不到参数,微信支付报缺少参数错误。

七、支付中遇到的问题

       小程序支付成功后,不点击完成,不会进入到success回调,怎么解决?

(不知道大家有没有遇见过这个问题,解决办法其实有很多种,反正我是踩了一次这个坑,虽然已经解决了,印象很深刻,貌似好像只是针对于扫呗支付我才遇见过,其他的支付还没有踩坑,大家可以思考一下!)

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值