PC网站和uniapp安卓APP、H5接入支付宝支付

首先我们需要完成支付宝账号注册,支持的账号类型:支付宝企业账号支付宝个人账号个体工商户

到支付宝商家平台 产品中心开通APP支付、手机网站支付、电脑网站支付的产品权限。

一、电脑PC网站接入

电脑PC网站支付是指商户在电脑网页展示商品或服务,用户在商户页面确认使用支付宝支付时,浏览器自动跳转支付宝电脑网页完成付款的支付产品。

签约申请提交材料要求如下:

  • 提供网站地址,网站能正常访问且页面显示完整,网站需要明确经营内容且有完整的商品信息。
  • 网站必须通过 ICP 备案,且备案主体需与支付宝账号主体一致。若网站备案主体与当前账号主体不同时需上传授权函。
  • 如以个人账号申请,需提供营业执照,且支付宝账号名称需与营业执照主体一致。

1、登录支付宝开放平台创建 网页/移动应用。应用类型选择网页应用,填入应用访问的网站地址。

2、配置接口加签方式、应用网关,详见接入准备

需要注意应用网关:用于接收支付宝异步通知消息,需要传入 http(s) 公网地址。不设置的话无法接受异步通知。支付宝网关地址:开发者调用 OpenAPI 发送 http(s) 请求的目标地址,需配置在AlipayClient中。

3、网页/移动应用:需要手动上线。提交审核后,预计 1 个工作日的审核时间。

4、绑定商家账号

在支付宝开放平台创建的应用归属于对应的开放平台账号。如果要在应用中使用支付和资金等相关产品,需要将应用和支付宝商家平台账号绑定,应用才可调用需要商家开通的产品。

5、集成支付宝SDK

java 复制代码
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.40.308.ALL</version>
        </dependency>

6、支付配置

7、接入 统一收单下单并支付页面接口

可以参考示例代码编写自己的代码:

java 复制代码
package com.ynfy.buss.mall.pay.service.alipay._native.impl;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.ynfy.buss.mall.pay.service.alipay._native.INativePayService;
import com.ynfy.common.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 支付宝电脑网站支付
 *
 * @author yangfeng
 * @since 2020/12/21 17:44
 */
@Slf4j
@Component
public class AliNativePayServiceImpl implements INativePayService {

    /**
     * app的id
     */
    @Value("${alipay.wapAppId}")
    private String wapAppId;

    /**
     * 私钥
     */
    @Value("${alipay.privateKey}")
    public String privateKey;

    /**
     * 公钥
     */
    @Value("${alipay.publicKey}")
    public String publicKey;

    /**
     * 支付回调地址
     */
    @Value("${alipay.notifyUrl}")
    String notifyUrl;

    /**
     * 网关地址
     */
    @Value("${alipay.serverUrl}")
    String serverUrl;


    @Override
    public Object pay(String orderSn, BigDecimal price, String description) {
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, wapAppId, privateKey, "json", "UTF-8", publicKey, "RSA2");
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setBody(description);
        model.setSubject(description);
        model.setOutTradeNo(orderSn);
        model.setProductCode("FAST_INSTANT_TRADE_PAY");
        // 设置PC扫码支付的方式
        model.setQrPayMode("1");
        // 设置请求后页面的集成方式
        model.setIntegrationType("PCWEB");
        model.setTimeoutExpress("30m");
        model.setTotalAmount(price.toString());
        request.setBizModel(model);
        request.setNotifyUrl(notifyUrl);
        try {
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request, "GET");
            return response.getBody();
        } catch (Exception e) {
            throw new JeecgBootException("支付宝电脑网站统一下单接口报错:" + HttpUtils.getErrorMsgFromWxPay(e.getMessage()));
        }
    }

}

8、iframe嵌入上述接口返回的连接,展示在自己的支付页面中。效果如下图:

用户支付完成后,触发回调通知,根据支付结果更新订单状态。回调接口代码:

java 复制代码
 @IgnoreAuth
    @ApiLog(value = "支付宝支付回调")
    @PostMapping("aliPayCallback")
    public Object aliPayCallback() throws Exception {
        log.info("支付宝支付回调通知...");
        //获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = iter.next();
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //调用SDK验证签名
        //公钥验签
        log.info("支付宝支付回调返回参数:{}", params);
        boolean signVerified = AlipaySignature.rsaCheckV1(params, publicKey, "UTF-8", "RSA2");
        if (signVerified) {
            log.info("支付宝支付验签成功...");
            List<PurchaseOrder> updateOrderList = new ArrayList<>();
            // 验签成功后
            // 获取支付结果参数
            String tradeStatus = request.getParameter("trade_status");
            log.info("支付宝支付回调交易状态:{}", tradeStatus);
            String outTradeNo = request.getParameter("out_trade_no");
            log.info("支付宝支付回调订单号:{}", outTradeNo);
            String gmtPayment = request.getParameter("gmt_payment");

            // 根据支付结果参数处理业务逻辑
            if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
                // 更新订单状态为支付成功
                orderService.updateOrderPayCallback(updateOrderList, CommonConstant.PAY_METHOD_ALI, outTradeNo, true, gmtPayment);
                log.info("支付宝支付回调通知处理完成,共更新{}个订单", updateOrderList.size());
                return "success";
            } else {
                // 更新订单状态为支付失败
                orderService.updateOrderPayCallback(updateOrderList, CommonConstant.PAY_METHOD_ALI, outTradeNo, false, null);
                return "fail";
            }
        }
        log.info("支付宝支付验签失败...");
        return "fail";
    }

然后在页面中定时器监听订单状态,通知用户支付是否成功。

二、uniapp手机网站(H5)接入支付

手机网站支付是指商家在移动端网页展示商品或服务,用户在商家页面确认使用支付宝支付后,浏览器自动跳转支付宝 App 或支付宝网页完成付款的支付产品。

1、登录支付宝开放平台创建 网页/移动应用,这里的应用类型也是网页应用,可以和上面的PC网页应用共享,所以不用再创建。

2、接入 手机网站支付接口2.0

可以参考示例代码编写自己的代码:

java 复制代码
package com.ynfy.buss.mall.pay.service.alipay.h5.impl;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.ynfy.buss.mall.pay.service.alipay.h5.IH5PayService;
import com.ynfy.common.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 支付宝H5支付
 *
 * @author yangfeng
 * @since 2020/12/21 17:44
 */
@Slf4j
@Component
public class AliH5PayServiceImpl implements IH5PayService {

    /**
     * app的id
     */
    @Value("${alipay.wapAppId}")
    private String wapAppId;

    /**
     * 私钥
     */
    @Value("${alipay.privateKey}")
    public String privateKey;

    /**
     * 公钥
     */
    @Value("${alipay.publicKey}")
    public String publicKey;

    /**
     * 支付回调地址
     */
    @Value("${alipay.notifyUrl}")
    String notifyUrl;

    /**
     * 网关地址
     */
    @Value("${alipay.serverUrl}")
    String serverUrl;

    @Override
    public Object pay(String orderSn, BigDecimal price, String description) {
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, wapAppId, privateKey, "json", "UTF-8", publicKey, "RSA2");
        // 构造请求参数以调用接口
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setSubject(description);
        model.setOutTradeNo(orderSn);
        // 设置产品码
        model.setProductCode("QUICK_WAP_WAY");
        model.setTimeoutExpress("30m");
        model.setTotalAmount(price.toString());
        request.setBizModel(model);
        request.setNotifyUrl(notifyUrl);
        try {
            AlipayTradeWapPayResponse response = alipayClient.pageExecute(request, "GET");
            return response.getBody();
        } catch (Exception e) {
            throw new JeecgBootException("支付宝h5统一下单接口报错:" + HttpUtils.getErrorMsgFromWxPay(e.getMessage()));
        }
    }

}

3、uniapp调用上述支付接口,跳转返回的链接页面代码:

javascript 复制代码
/**
 * 支付
 * @param response
 */
export function payment(response, clientType) {
	//app端需要判断操作系统是否为华为鸿蒙
	// #ifdef APP-PLUS
	if (isHarmonyOS()) {
		uni.showToast({
			icon: "none",
			title: "鸿蒙暂不可支付",
		});
		return
	}
	// #endif

	const provider = clientType == 'WxPay' ? "wxpay" : clientType == 'AliPay' ? 'alipay' : '';
	if (!provider) {
		uni.showToast({
			icon: "none",
			title: "支付方式缺失",
		});
		return
	}
	//app微信支付 orderInfo为json对象
	//app支付宝支付 orderInfo为字符串,https://uniapp.dcloud.net.cn/tutorial/app-payment-alipay.html
	let orderInfo = null;
	if (provider == "wxpay") {
		orderInfo = {
			appid: response.appid,
			noncestr: response.nonceStr,
			package: response.packageVal,
			partnerid: response.partnerId,
			prepayid: response.prepayId,
			timestamp: response.timestamp,
			sign: response.sign
		}
	} else if (provider == "alipay") {
		orderInfo = response
	}
	uni.requestPayment({
		provider: provider,
		// #ifdef APP-PLUS
		orderInfo: orderInfo,
		// #endif
		// #ifdef MP-WEIXIN
		timeStamp: response.timeStamp,
		nonceStr: response.nonceStr,
		package: response.packageVal,
		signType: response.signType,
		paySign: response.paySign,
		// #endif
		success: (e) => {
			uni.showToast({
				icon: "none",
				title: "支付成功!",
			});
			setTimeout(() => {
				uni.navigateTo({
					url: "/subpages/myorder/myorder"
				})
			}, 1000)
		},
		fail: (e) => {
			uni.showModal({
				title: "支付失败",
				content: "如果您已支付,请勿反复支付",
				showCancel: false,
				success: () => {
					uni.redirectTo({
						url: "/subpages/myorder/myorder?orderStatus=0",
					});
				},
			});
		},
	});
}

export function isHarmonyOS() {
	const sysInfo = uni.getSystemInfoSync();
	// 检查版本号是否包含"HarmonyOS"或"10+"等标识
	return /(10\+|\+HarmonyOS)/.test(sysInfo.system);
}

// 去支付
export async function goPay(orderSn, clientType) {
	const orderData = await apiGetOrder(orderSn);
	const payParams = {
		clientType: clientType,
		// #ifdef MP-WEIXIN
		paymentType: "MP",
		// #endif
		// #ifdef H5
		paymentType: "H5",
		// #endif
		// #ifdef APP-PLUS
		paymentType: "APP",
		// #endif
		price: orderData.orderPrice,
		orderSn: orderSn,
		description: orderData.orderTitle
	};
	const response = await apiPay(payParams);
	// #ifdef MP-WEIXIN || APP-PLUS
	payment(response, clientType)
	// #endif
	// #ifdef H5
	if (clientType == 'WxPay') {
		window.location.href = response.h5Url
	} else if (clientType == 'AliPay') {
		window.location.href = response
	}
	// #endif
}

4、打开支付宝APP完成付款。效果如下:

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | |

三、uniapp安卓APP支付

APP支付是指商家在商家移动端 App 中集成支付宝 SDK,调起支付宝来完成付款的一种支付产品。适用于在商家移动端 App 内使用支付宝支付功能的场景。

签约申请提交材料要求如下:

  • 如应用已上架,需提供应用名称和已上架的应用市场;若应用未上架,需提供 Demo 或产品说明文档。
  • 应用必须包含明确的经营内容和价格信息;且经营内容须与营业执照经营范围一致。
  • 如应用开发者与支付宝账号名称不一致需提供开发者授权函。
  • 个人账号申请,需提供营业执照,且支付宝账号名称需与营业执照主体一致。

1、登录 支付宝开放平台,点击创建 网页/移动应用。(仅支持应用类型为 移动应用 接入)。

2、配置接口加签方式、应用网关,详见接入准备

3、网页/移动应用:需要手动上线。提交审核后,预计 1 个工作日的审核时间。

4、绑定商家账号

在支付宝开放平台创建的应用归属于对应的开放平台账号。如果要在应用中使用支付和资金等相关产品,需要将应用和支付宝商家平台账号绑定,应用才可调用需要商家开通的产品。

5、接入 app支付接口2.0

可以参考示例代码编写自己的代码:

java 复制代码
package com.ynfy.buss.mall.pay.service.alipay.app.impl;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.ynfy.buss.mall.pay.service.alipay.app.IAppPayService;
import com.ynfy.common.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 支付宝APP支付
 *
 * @author yangfeng
 * @since 2020/12/21 17:44
 */
@Slf4j
@Component
public class AliAppPayServiceImpl implements IAppPayService {

    /**
     * app的id
     */
    @Value("${alipay.appAppId}")
    private String appId;

    /**
     * 私钥
     */
    @Value("${alipay.privateKey}")
    public String privateKey;

    /**
     * 公钥
     */
    @Value("${alipay.publicKey}")
    public String publicKey;

    /**
     * 支付回调地址
     */
    @Value("${alipay.notifyUrl}")
    String notifyUrl;

    /**
     * 网关地址
     */
    @Value("${alipay.serverUrl}")
    String serverUrl;


    @Override
    public Object pay(String orderSn, BigDecimal price, String description) {
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, "json", "UTF-8", publicKey, "RSA2");
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(description);
        model.setOutTradeNo(orderSn);
        // 设置产品码
        model.setProductCode("QUICK_MSECURITY_PAY");
        model.setTimeoutExpress("30m");
        model.setTotalAmount(price.toString());
        request.setBizModel(model);
        request.setNotifyUrl(notifyUrl);
        try {
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            return response.getBody();
        } catch (Exception e) {
            throw new JeecgBootException("支付宝APP统一下单接口报错:" + HttpUtils.getErrorMsgFromWxPay(e.getMessage()));
        }
    }

}

6、uniapp调用上述接口,调起支付宝支付控件。效果如图:

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | |