对接支付宝服务商当面付&手机网页支付

一、前期准备:

SpringBoot对接支付宝当面付和手机网站支付_springboot 支付宝当面付_Biubiubiuexo的博客-CSDN博客

 配置成功后获得到我们开发需要的:支付宝公钥、商户私钥、应用ID

二、代码实现:

1.新建Merchant商户实体类:

package com.yuheng.payment.models;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
 * 收款商户
 * @author michael
 * @since  2022/11/15
 */
@Getter
@Setter
@Builder
@AllArgsConstructor
public class Merchant {
    /**
     * 应用appID
     */
    private String appId;
    /**
     * 应用密钥
     */
    private String alipayAppKey;
    /**
     * 商户密钥
     */
    private String mchKey;

    /**
     * 第三方应用商户token(若为空则为普通支付模式,不为空为服务商代调用模式)
     */
    private String appAuthToken;

}

 2.支付宝服务层:

package com.yuheng.payment.services;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import com.yuheng.payment.constants.PayStatus;
import com.yuheng.payment.exceptions.InternalRuntimeException;
import com.yuheng.payment.exceptions.PrePayResponseException;
import com.yuheng.payment.models.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Map;

import static java.lang.System.out;

/**
 * 支付宝官方
 * @author michael
 * @since  2022/11/15
 */
public class ZhifubaoService implements SuperService {
    private static final Logger log = LoggerFactory.getLogger(ZhifubaoService.class);

    private static String serviceUrl = "https://openapi.alipay.com/gateway.do";


    /**
     * 支付宝扫码支付
     * @param request
     * @param merchant
     * @return
     * @throws AlipayApiException
     */
    @Override
    public PrePayResponse zfbQr(PrePayRequest request, Merchant merchant) throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
                merchant.getAppId(),merchant.getMchKey(),"json","UTF-8",merchant.getAppKey(),"RSA2");
        AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
        alipayTradePrecreateRequest.setReturnUrl(request.getReturnUrl());
        alipayTradePrecreateRequest.setNotifyUrl(request.getNotifyUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", request.getOrderNo());
        // 传入金额单位为 分,换成支付宝单位 元
        bizContent.put("total_amount", Double.toString(request.getAmount() / 100.00));
        bizContent.put("subject", request.getBody());
        alipayTradePrecreateRequest.setBizContent(bizContent.toString());
        String resultStr = null;
        // 为第三方应用代调用模式
        if (StrUtil.isNotEmpty(merchant.getAppAuthToken())){
            alipayTradePrecreateRequest.putOtherTextParam("app_auth_token", merchant.getAppAuthToken());
            resultStr = alipayClient.execute(alipayTradePrecreateRequest,null,merchant.getAppAuthToken()).getBody();
        }else {
            // 普通商家模式
            resultStr = alipayClient.execute(alipayTradePrecreateRequest).getBody();
        }
        JSONObject jsonObject = JSONObject.parseObject(resultStr).getJSONObject("alipay_trade_precreate_response");
        String code = jsonObject.getString("code");
        if (!"10000".equals(code)){
            log.error("支付发起失败{}", jsonObject.getString("sub_msg"));
        }
        String qrCode = jsonObject.getString("qr_code");
        String outTradeNo = jsonObject.getString("out_trade_no");
//        return jsonObject.getJSONObject("alipay_trade_precreate_response").getString("qr_code");
        return PrePayResponse
                .builder()
                .orderNo(outTradeNo)
                .isRedirect(false)
                .payText(qrCode)
                .payStatus(PayStatus.PAYING)
                .build();
    }


    /**
     * 手机网页支付
     * @param
     * @param merchant
     * @param request
     * @return
     * @throws AlipayApiException
     */
    @Override
    public PrePayResponse zfbWeb(PrePayRequest request, Merchant merchant) throws AlipayApiException, IOException {
        AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
                merchant.getAppId(),merchant.getMchKey(),"json","UTF-8",merchant.getAppKey(),"RSA2");
        AlipayTradeWapPayRequest alipayTradeWapPayRequest = new AlipayTradeWapPayRequest();
        alipayTradeWapPayRequest.setNotifyUrl(request.getNotifyUrl());
        alipayTradeWapPayRequest.setReturnUrl(request.getReturnUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", request.getOrderNo());
        // 传入金额单位为 分,换成支付宝单位 元
        bizContent.put("total_amount", Double.toString(request.getAmount() / 100.00));
        bizContent.put("subject", request.getBody());
        bizContent.put("product_code", "QUICK_WAP_WAY");
        alipayTradeWapPayRequest.setBizContent(bizContent.toString());
        // 为第三方应用代调用模式
        if (StrUtil.isNotEmpty(merchant.getAppAuthToken())){
            alipayTradeWapPayRequest.putOtherTextParam("app_auth_token", merchant.getAppAuthToken());
        }
        AlipayTradeWapPayResponse alipayTradeWapPayResponse = alipayClient.pageExecute(alipayTradeWapPayRequest,"get");
        String path = alipayTradeWapPayResponse.getBody();
        return PrePayResponse
                .builder()
                .payText(path)
                .isRedirect(false)
                .payStatus(PayStatus.PAYING)
                .build();
    }

    /**
     * @param request
     * 支付宝异步通知
     * @return
     */
    @Override
    public PayNotify payNotify(HttpServletRequest request) {
        // 获取支付宝POST过来反馈信息
        Map<String, String> params = AliPayApi.toMap(request);
        log.debug("收到支付宝支付原始通知:"+params);
        PayNotify notifyResponse = PayNotify.builder()
                .orderNo(params.get("out_trade_no"))
                .outOrderNo(params.get("trade_no"))
                .payStatus(PayStatus.PAYING)
                .build();
        if ("TRADE_SUCCESS".equals( params.get("trade_status"))) {
            notifyResponse.setPayTime(timeFormat(params.get("gmt_payment")));
            notifyResponse.setPayStatus(PayStatus.PAID);
        }
        notifyResponse.setRawSignObject(params);
        return notifyResponse;
    }

    /**
     * 签名验证
     * @param response
     * @param merchant
     * @return
     * @throws AlipayApiException
     */
    @Override
    public boolean verifyPayNotify(PayNotify response, Merchant merchant) throws AlipayApiException {
        return AlipaySignature.rsaCheckV1((Map<String, String>) response.getRawSignObject(), merchant.getAppKey(), "UTF-8", "RSA2");
    }


    /**
     * 根据订单号进行查询
     * @param orderNo 内部订单编号
     * @param merchant 商户信息
     * @return
     * @throws AlipayApiException
     */
    @Override
    public PayResult payOrderQuery(String orderNo, Merchant merchant) throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
                merchant.getAppId(),merchant.getMchKey(),"json","GBK",merchant.getAppKey(),"RSA2");
        AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", orderNo);
        alipayTradeQueryRequest.setBizContent(bizContent.toString());
        alipayTradeQueryRequest.putOtherTextParam("app_auth_token",merchant.getAppAuthToken());
        String body =  alipayClient.execute(alipayTradeQueryRequest).getBody();
        JSONObject jsonObject = JSONObject.parseObject(body).getJSONObject("alipay_trade_query_response");
        String code = jsonObject.getString("code");
        if (!"10000".equals(code)){
            throw new InternalRuntimeException("发起查询失败,原因["+code+":"+ jsonObject.getString("sub_msg")+"]");
        }
        String tradeStatus = jsonObject.getString("trade_status");
        PayStatus payStatus;
        switch (tradeStatus){
            case "TRADE_FINISHED" :
                payStatus = PayStatus.FINISHED;
                break;
            case "TRADE_SUCCESS" :
                payStatus = PayStatus.PAID;
                break;
            case "TRADE_CLOSED" :
                payStatus = PayStatus.CLOSE;
                break;
            default:
                payStatus = PayStatus.PAYING;
        }
        return PayResult
                .builder()
                .outOrderNo(jsonObject.getString("trade_no"))
                .orderNo(jsonObject.getString("out_trade_no"))
                .payStatus(payStatus)
                .build();
    }


    /**
     * 时间戳转时间字符串
     * @param timeStr 时间戳Long
     * @return String 时间
     */
    private Long timeFormat(String timeStr){
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        try {
            return dateFormat.parse(timeStr).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 时间戳转时间字符串
     * @param time 时间戳Long
     * @return String 时间
     */
    private String timeFormat(Long time){
        return new SimpleDateFormat("yyyyMMddHHmmss").format(time);
    }

}

3.支付宝实现层:

private static Merchant getMerchant(){
        return Merchant.builder()
                .appId("服务商的应用ID")
                .mchKey("服务商的商户密钥")
                .alipayAppKey("服务商的支付宝公钥")
                .appAuthToken("商户授权成功后得到的TOKEN")
                .build();
    }

    /**
     * 二维码扫码支付
     */
    @Test
    public void testPay(){
        Client client = Client.newInstance(PaySuper.ALIPAY);
        PrePayRequest request = PrePayRequest.builder()
                .amount(1)
                .body("支付宝扫码支付测试")
                .payMethod(PayMethod.ZFB_QR)
                .notifyUrl("你的异步通知地址")
                .build();
        PrePayResponse response = client.prePay(request,getMerchant());
        System.out.println(JSONUtil.toJsonStr(response));
    }

    /**
     * 根据外部订单号进行查询
     */
    @Test
    public void testQuery(){
        Client client = Client.newInstance(PaySuper.ALIPAY);
        PayResult response = client.payOrderQuery("订单号",getMerchant());
        System.out.println(JSONUtil.toJsonStr(response));
    }

    /**
     * 注意:订单创建实际创建时间也为用户支付时间,非唤起收银台时间。
     * @param
     */
    @Test
    public void wapPay(){
		Client client = Client.newInstance(PaySuper.ALIPAY);
		PrePayRequest request = PrePayRequest.builder()
			.amount(1)
			.body("支付宝网页支付测试")
			.payMethod(PayMethod.ZFB_WEB)
			.returnUrl("你的回调地址")
			.notifyUrl("你的异步通知地址")
			.build();
		PrePayResponse prePayResponse = client.prePay(request, getMerchant());
        System.out.println(JSONUtil.toJsonStr(prePayResponse));
    }

    @Test
    public static void main(String[] args) throws AlipayApiException {
        Client client = Client.newInstance(PaySuper.ALIPAY);
        //回调的待验签字符串
        String resultInfo = "异步通知地址返回的通知信息";
                //对待签名字符串数据通过&进行拆分
        String [] temp = resultInfo.split("&");
        HashMap<String, String> map =  new HashMap<String, String>();
        //把拆分数据放在map集合内
        for (int i = 0; i < temp.length; i++) {
            String[] arr = temp[i].split("=", 2); //通过"="号分割成2个数据
            String[] tempAagin = new String[arr.length]; //再开辟一个数组用来接收分割后的数据
            for (int j = 0; j < arr.length; j++) {
                tempAagin[j] = arr[j];
            }
            map.put(tempAagin[0], tempAagin[1]);
        }
        System.out.println(map);
        //验签方法
        PayNotify payNotify = PayNotify.builder().rawSignObject(map).build();
        boolean signVerified = client.verifyPayNotify(payNotify,getMerchant());
        if(signVerified){
            // TODO 验签成功后
            System.out.println("success");
        }else{
            System.out.println("fail");
        }
    }

4.商户授权服务商平台 :授权流程https://opensupport.alipay.com/support/helpcenter/198/201602494631?ant_source=manual&recommend=9784af7814a1ec663089e796d8a7b4ce

   (1)拼接授权URL:

public String AuthorizationUrl(String mchId,String outAppId) throws AlipayApiException {
        // 第一步拼接授权地址,商户扫码进行授权
        com.yuheng.payment.models.Merchant serviceMerchant = getServiceMerchant(outAppId);
        AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
                serviceMerchant.getAppId(),serviceMerchant.getMchKey(),"json","GBK",serviceMerchant.getAppKey(),"RSA2");
        AlipayOpenAuthAppauthInviteCreateRequest request = new AlipayOpenAuthAppauthInviteCreateRequest();
        String redirectUrl = getRedirectUrl();
        JSONObject bizContent = new JSONObject();
        bizContent.put("auth_app_id",serviceMerchant.getAppId());
        bizContent.put("redirect_url",redirectUrl);
        request.setBizContent(bizContent.toString());
        AlipayOpenAuthAppauthInviteCreateResponse response = alipayClient.execute(request);
        System.out.println("::::::::::::" + response.getTaskPageUrl());
        return "https://openauth.alipay.com/oauth2/appToAppAuth.htm?app_id="
                + serviceMerchant.getAppId()  // 第三方应用ID
                +"&redirect_uri="
                +"http://192.168.1.189:8005/api/merchant/redirect"
                +"&state="
                +mchId;
        /*return "https://openauth.alipay.com/oauth2/appToAppAuth.htm?app_id="
                + appId  // 第三方应用ID
                +"&redirect_uri="
                +"https://xqht.hnyuheng.net/";*/
    }

 @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAppAuthTokenById(String appAuthToken, String id) {
        merchantRepository.addAppAuthTokenById(appAuthToken,id);
    }

    @Override
    public MerchantDto selectServiceMerchant(String appId) {
        Merchant merchant = merchantRepository.findByAppIdAndIsService(appId,true);
        return merchantMapper.toDto(merchant);
    }

    private String getRedirectUrl(){
        return RequestHolder.getAppUrl() + "/api/merchant/redirect/";
    }

     (2)商户点击链接或扫码成功授权后回调地址返回app_auth_token(回调地址一定要与第三方应用配置的回调地址一模一样)

       授权回调接口:

package com.yuheng.modules.mch.rest;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayOpenAuthTokenAppRequest;
import com.alipay.api.response.AlipayOpenAuthTokenAppResponse;
import com.yuheng.annotation.AnonymousAccess;
import com.yuheng.exception.BadRequestException;
import com.yuheng.modules.mch.constant.PayType;
import com.yuheng.modules.mch.domain.MchApp;
import com.yuheng.modules.mch.service.MchAppService;
import com.yuheng.modules.mch.service.MerchantService;
import com.yuheng.modules.mch.service.dto.MerchantDto;
import com.yuheng.payment.constants.PaySuper;
import com.yuheng.payment.models.Merchant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 支付宝商户授权成功后返回地址
 * @author liuzhaojun
 * @createDate 2023-03-29 10:13
 */
@Slf4j
@RestController
@RequestMapping("/api/merchant/redirect")
@RequiredArgsConstructor
public class AlipayRedirectURLController {
    // 服务环境
    @Value("${alipay.serviceUrl}")
    private String serviceUrl;
    private final MerchantService merchantService;
    private final MchAppService mchAppService;

    @GetMapping
    @AnonymousAccess
    public ResponseEntity<Object> redirectURL(@RequestParam(value = "app_id") String outAppId, @RequestParam("state") String mchId, @RequestParam("source") String source, @RequestParam("app_auth_code") String appAuthCode) throws AlipayApiException {
        // 商户同意授权之后支付宝回调参数--->用appAuthCode换取appAuthToken
        Merchant serviceMerchant = getServiceMerchant(outAppId);
        if (ObjectUtil.isNull(serviceMerchant)) throw new BadRequestException("未查到服务商信息");
        AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
                outAppId,serviceMerchant.getMchKey(),"json","GBK",serviceMerchant.getAppKey(),"RSA2");
        System.out.println("app_id=" + outAppId + ";source=" + source + ";app_auth_code=" +appAuthCode + ";state=" + mchId);
        JSONObject bizContent = new JSONObject();
        // 授权方式为通过授权码授权
        bizContent.put("grant_type","authorization_code");
        bizContent.put("code",appAuthCode);
        AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest();
        request.setBizContent(bizContent.toString());
        AlipayOpenAuthTokenAppResponse response = alipayClient.execute(request);
        if(response.isSuccess()){
            System.out.println("Token:::::::::::" + response.getAppAuthToken());
            System.out.println("RefreshToken:::::::::::" + response.getAppRefreshToken());
            // 新增字段app_auth_token
            merchantService.updateAppAuthTokenById(response.getAppAuthToken(),mchId);
            // 将商户状态改为激活
            merchantService.enable(mchId);
            return new ResponseEntity<>(HttpStatus.OK);
        } else {
            String errorInfo = JSONObject.toJSONString(response);
            log.error("调用失败" + errorInfo);
            return new ResponseEntity<>(errorInfo,HttpStatus.BAD_REQUEST);
        }
    }

    private Merchant getServiceMerchant(String outAppId){
        // 此处appId是支付宝应用ID (应用表的外部应用ID) => 查出上游为支付宝且绑定了支付宝第三方应用的商户信息
        MchApp mchApp = mchAppService.findByOutAppId(outAppId, PaySuper.ALIPAY.getName(), PayType.ALIPAY_THIRD.getCode());
        // 查出服务商信息(以字段is_service为判断依据)
        MerchantDto merchantDto = merchantService.selectServiceMerchant(mchApp.getId());
        return new Merchant(mchApp.getOutAppId(), mchApp.getOutAppSecret(),
                merchantDto.getOutMchId(),merchantDto.getOutMchKey(),"");
    }

}

关于回调地址:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值