1

聚合支付项目-40_TheEye的技术博客_51CTO博客

 4 weeks ago
source link: https://blog.51cto.com/u_16228353/10518736
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

3.4 生成二维码

3.4.1 系统交互流程

生成二维码的系统交互 流程如下:

1、商户登录商户应用平台 ,查询门店列表

2、商户平台 请求交易 服务获取门店二维码URL

3、商户平台 根据 URL生成二维码

3.4.2 交易服务生成二维码URL

3.4.2.1 接口定义

接口描述:

生成门店的c扫b二维码

接口参数:

输入:商户id、应用id、门店id、标题,内容

输出:支付入口

1、在交易服务的api工程 创建dto

@Data
@NoArgsConstructor
public class QRCodeDto implements Serializable {
    private Long merchantId;
    private String appId;
    private Long storeId;
    private String subject;//商品标题
    private String body;//订单描述
}

http://172.16.33.100:56010/pay-entry/

2、接口定义如下

/**
\* 交易订单相关服务接口
*/
public interface TransactionService {
    /**
    \* 生成门店二维码
    \* @param qrCodeDto,传入merchantId,appId、storeid、channel、subject、body
    \* @return 支付入口URL,将二维码的参数组成json并用base64编码
    \* @throws BusinessException
    */
    String createStoreQRCode(QRCodeDto qrCodeDto) throws BusinessException;
}
3.4.2.2 商户服务应用合法校验接口

商户生成二维码需要根据门店、应用来生成,设计接口需要对应用和门店的合法性来校验。

1、校验该应用是否属于该商户。

2、校验该门店是否属于该商户

1、接口描述

1)根据商户id和应用id查询应用信息,查询到则说明合法。

2、在AppService下定义接口如下

/**
\* 查询应用是否属于某个商户
\* @param appId
\* @param merchantId
\* @return
*/
Boolean queryAppInMerchant(String appId, Long merchantId);

3、接口实现如下

在AppServiceImpl中实现queryAppInMerchant接口

/**
     *  查询应用是否属于某个商户
     * @author glls
     * @param appId  应用id
     * @param merchantId  商户id
     * @return java.lang.Boolean
     * @date 2021/3/3 20:18
     */
@Override
public Boolean queryAppInMerchant(String appId, Long merchantId) {
    Integer count = appMapper.selectCount(new LambdaQueryWrapper<App>()
                                          .eq(App::getAppId, appId).eq(App::getMerchantId, merchantId));
    return count>0;
}
3.4.2.3 商户服务门店合法校验接口

1、接口描述

1)根据商户id和门店id查询门店,查询到则说明合法。

2、在MerchantService中定义接口:

/**
\* 查询门店是否属于某商户
\* @param storeId
\* @param merchantId
\* @return
*/
Boolean queryStoreInMerchant(Long storeId, Long merchantId);

3、接口实现如下

在MerchantServiceImpl中实现queryStoreInMerchant接口

/**
\* 查询门店是否属于某商户
\* @param storeId
\* @param merchantId
\* @return
*/
@Override
public Boolean queryStoreInMerchant(Long storeId, Long merchantId) {
    Integer count = storeMapper.selectCount(new LambdaQueryWrapper<Store>()
                                            .eq(Store::getId,storeId).eq(Store::getMerchantId, merchantId));
    return count>0;
}
3.4.2.4 接口实现

在Nacos中添加支付入口配置:transaction-service.yaml 支付入口是扫码支付的统一入口。

# 支付入口url 
huiminpay:
  payurl: "http://127.0.0.1:56010/transaction/pay-entry/"

实现createStoreQRCode接口:

package com.huiminpay.transaction.service;

import com.alibaba.fastjson.JSON;
import com.huiminpay.common.domain.BusinessException;
import com.huiminpay.common.domain.CommonErrorCode;
import com.huiminpay.common.util.EncryptUtil;
import com.huiminpay.merchant.api.AppService;
import com.huiminpay.merchant.api.MerchantService;
import com.huiminpay.transaction.api.TransactionService;
import com.huiminpay.transaction.api.dto.PayOrderDTO;
import com.huiminpay.transaction.api.dto.QRCodeDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;

/**
 * @ClassName : TransactionServiceImpl
 * @Author : glls
 * @Date: 2021/3/3 20:27
 * @Description :
 */
@Slf4j
@DubboService
public class TransactionServiceImpl implements TransactionService {
    /**
     * 支付入口url
     */
    @Value("${huiminpay.payurl}")
    private String payurl;

    @DubboReference
    MerchantService merchantService;

    @DubboReference
    AppService appService;

    /**
     * 生成门店二维码
     * @author glls
     * @param qrCodeDto 商户id  应用id 门店id  标题  内容
     * @return java.lang.String
     * @date 2021/3/3 20:30
     */
    @Override
    public String createStoreQRCode(QRCodeDto qrCodeDto) throws BusinessException {
        // 校验应用和门店
        verifyAppAndStore(qrCodeDto.getMerchantId(),qrCodeDto.getAppId(),qrCodeDto.getStoreId());

        //1. 生成支付信息
        PayOrderDTO payOrderDTO = new PayOrderDTO();
        payOrderDTO.setMerchantId(qrCodeDto.getMerchantId());
        payOrderDTO.setAppId(qrCodeDto.getAppId());
        payOrderDTO.setStoreId(qrCodeDto.getStoreId());
        payOrderDTO.setSubject(qrCodeDto.getSubject());//显示订单标题
        payOrderDTO.setChannel("huimin_c2b");//服务类型
        payOrderDTO.setBody(qrCodeDto.getBody());//订单内容
        String jsonString = JSON.toJSONString(payOrderDTO);
        log.info("transaction service createStoreQRCode,JsonString is {}",jsonString);

        // 将支付信息保存到票据中
        String ticket = EncryptUtil.encodeUTF8StringBase64(jsonString);
        //支付入口
        String payEntryUrl = payurl +ticket;
        log.info("transaction service createStoreQRCode,pay‐entry is {}",payEntryUrl);
        return payEntryUrl;

    }

    /**
     * 校验应用和门店是否属于当前登录商户
     * @param merchantId
     * @param appId
     * @param storeId
     */
    private void verifyAppAndStore(Long merchantId,String appId,Long storeId) {
        //判断应用是否属于当前商户
        Boolean contains = appService.queryAppInMerchant(appId, merchantId);
        if (!contains) {
            throw new BusinessException(CommonErrorCode.E_200005);
        }
        //判断门店是否属于当前商户
        Boolean containsStore = merchantService.queryStoreInMerchant(storeId, merchantId);
        if (!containsStore) {
            throw new BusinessException(CommonErrorCode.E_200006);
        }
    }
}
package com.huiminpay.transaction.api.dto;

import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 下单请求对象
 */
@Data
public class PayOrderDTO implements Serializable {

    private Long merchantId;//商户id
    private Long storeId;//商户下门店,如果未指定,默认是根门店
    private String appId;//此处使用业务id,服务内部使用主键id,服务与服务之间用业务id--appId
    private String channel;//聚合支付的渠道 此处使用渠道编码
    private String tradeNo;//聚合支付平台订单号
    private String outTradeNo;//商户订单号
    private String subject;//商品标题
    private String body;//订单描述
    private String currency;//币种CNY
    private Integer totalAmount;//订单总金额,单位为分
    private String optional;//自定义数据
    private String analysis;//用于统计分析的数据,用户自定义
    private String extra;//特定渠道发起时额外参数
    private String openId;//c端付款用户身份标识
    private String authCode;//付款条码,支付宝或者微信点“付款”产生的付款条码    在b扫c时,应由前端传过来
    private String device;//设备,存放UA等信息
    private String clientIp;//客户端id

    private String payChannel;//支付渠道
    private String tradeState;//订单状态

    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
    private LocalDateTime expireTime;//订单过期时间
    private LocalDateTime paySuccessTime;//支付成功时间
}

3.4.3 商户平台应用生成二维码

3.4.3.1 接口实现

1、配置参数

定义c扫b二维码的标题和内容

内容 如下:

huiminpay:
  c2b:
    subject: "%s的商品"
    body: "向%s付款"

2、定义生成商户应用门店二维码接口

createCScanBStoreQRCode

package com.huiminpay.merchant.controller;

import com.huiminpay.common.domain.BusinessException;
import com.huiminpay.common.domain.CommonErrorCode;
import com.huiminpay.common.domain.PageVO;
import com.huiminpay.common.util.QRCodeUtil;
import com.huiminpay.merchant.api.MerchantService;
import com.huiminpay.merchant.api.dto.MerchantDTO;
import com.huiminpay.merchant.api.dto.StoreDTO;
import com.huiminpay.merchant.common.util.SecurityUtil;
import com.huiminpay.transaction.api.TransactionService;
import com.huiminpay.transaction.api.dto.QRCodeDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

/**
 * @ClassName : StoreController
 * @Author : glls
 * @Date: 2021/3/3 19:14
 * @Description : 门店管理
 */
@Api(value = "商户平台‐门店管理", tags = "商户平台‐门店管理", description = "商户平台‐门店的增删改查")
@RestController
@Slf4j
public class StoreController {

    @DubboReference
    private MerchantService merchantService;

    @DubboReference
    private TransactionService transactionService;

    /**
     * 门店二维码订单标题
     */
    @Value("${huiminpay.c2b.subject}")
    private String subject;
    /**
     * 门店二维码订单内容
     */
    @Value("${huiminpay.c2b.body}")
    private String body;

    @ApiOperation("生成商户应用门店二维码")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "appId", value = "商户应用id", required = true, dataType = "String", paramType = "path"),
        @ApiImplicitParam(name = "storeId", value = "商户门店id", required = true, dataType = "String", paramType ="path")
    })
    @GetMapping(value = "/my/apps/{appId}/stores/{storeId}/app-store-qrcode")
    public String createCScanBStoreQRCode(@PathVariable String appId, @PathVariable Long storeId) throws BusinessException {
        //商户id
        Long merchantId = SecurityUtil.getMerchantId();
        //商户信息
        MerchantDTO merchantDTO = merchantService.queryMerchantById(merchantId);

        //生成二维码链接
        QRCodeDto qrCodeDto = new QRCodeDto();
        qrCodeDto.setMerchantId(merchantId);
        qrCodeDto.setAppId(appId);
        qrCodeDto.setStoreId(storeId);
        //标题   用商户名称替换 %s
        String subjectFormat = String.format(subject, merchantDTO.getMerchantName());
        qrCodeDto.setSubject(subjectFormat);
        //内容,格式:"向%s 付款"
        String bodyFormat = String.format(body, merchantDTO.getMerchantName());
        qrCodeDto.setBody(bodyFormat);
        //获取二维码的URL
        String storeQRCodeUrl = transactionService.createStoreQRCode(qrCodeDto);
        log.info("[merchantId:{},appId:{},storeId:{}]createCScanBStoreQRCode is {}",merchantId,appId,storeId,storeQRCodeUrl);
        try {
            //根据返回url,调用生成二维码工具类,生成二维码base64返回
            QRCodeUtil qrCodeUtil = new QRCodeUtil();
            //二维码图片base64编码
            String qrCode = qrCodeUtil.createQRCode(storeQRCodeUrl, 200, 200);
            return qrCode;
        } catch (IOException e) {
            throw new BusinessException(CommonErrorCode.E_200007);
        }
    }
}
3.4.3.2 测试

1、请求认证,获取token及租户id

2、请求获取二维码

注意:应用及门店的合法性


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK