> 文档中心 > 支付宝支付(详细版)

支付宝支付(详细版)


支付宝支付

一、准备

1、注册支付宝开发者、配置沙箱环境

1.1、配置沙箱账号

支付宝支付(详细版)

1.2、配置沙箱应用

支付宝支付(详细版)

配置公钥私钥需要下载支付宝开发者工具生成应用公钥,再去开发者平台将环境绑定

支付宝支付(详细版)

1.3、手机下载沙箱版支付宝

支付宝支付(详细版)

2、公网地址准备

在用户扫码支付成功后,支付平台会调用回调地址,将支付结果返回商家系统。所以必须保证你的配的的回调地址,在公网可以访问的

常用的技术:内网穿透(可以使用花生壳、sunny-ngrok等工具)

二、支付流程

支付宝支付(详细版)

三、spring boot 集成

3.1、依赖导入

<dependency>    <groupId>com.alipay.sdk</groupId>    <artifactId>alipay-sdk-java</artifactId>    <version>4.13.0.ALL</version></dependency>

3.2、配置文件绑定

3.2.1、application.yml配置

# 支付宝支付参数配置alipay:  # 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号  app_id: 2021000117697  # 商户私钥,您的PKCS8格式RSA2私钥  merchant_private_key: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCV5x7VamtaP1knZTtJ/5taEO9+14iy8YWhu272Ey2OvNkozbVUCzz6QY/J8GLxomqdysj9WlMbEs5spB8aNdxPVt5Eue5pFE9L5AUYdIw/cuVnwh9rGTk/13LEnyrJc0q59gvQqB4jylojreo6LcXdobQq4cO83Q9QKHLqDgjK7jzFm4B+VEPMSfp0Y0SVmayw6J3mv54E4WJQ7P5m7IbzfV5wY0PxBrcR4WvJFAkX3uPbxLoPYrJfLNEES5jxCoy5tolk4UApli2cUEpKCCcY2knMxrJGaqW+n27d7ucyGG6nkPwo5VulUiq5t+8VxpjwEiGSkOUPTa4I7kOgrL4nAgMBAAECggEATAAIzvUMho31mpalj6frUKur0Jy+bqxwKBgHyZTK2YiFYgFNEGKGlAuOq63uSJrMDRBQtA3d9w9Ca7ySUP1ulObxUflzVmXiOO/0fNv+HDczk12I4hWIlVR42DOdG5c/A7iBq3va5XvVTjk5PszDKsRm2hnkVZ/SYxwrRHaJ0+UuBwpZn+wkvhd5oMySyuBDhRKB1ezou+szbhAoGAR5znCOQvV96WS59Bwp8I7BuATPOCUgV4bweMTQ79PLMGmIGU3Sh22DFnUD3+jEmKBSFyFEC6MWa9PaEURgJsUEKZpLkPyJ95NuObfvDAbIRfjQrw5mVfQ2KaHvATIvdesb1hOCZ66xG4yKwtQnJkS1LPm3SV+SvWAEmqbhOiLfkCgYA+pVhJ0SeRHMB09LCJvkugBttiE84H3aR2s7AN4AlArVGqTB6uoZzewdq/bTUo6KHF0EOeismEYDIhFC6ax7Gdy/j/TAcQLusg6W9NmKr0OUxqMCilwoUZkp7fZT3HMG+hL50lkyAw46MHG1uENUCp47lo7MPB+fmWGh9hSN9JcQ==  # 支付宝公钥  alipay_public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lcHyBUi27zKgFCqwDXPxHEXNTNGFZQVsLRJQoKQx7zAkNKyCap0Q3Dpa6FTv5+pfj1ULId6z6epGA9nQ5oGdOHNaYY1pT10GqiXpECtyKVp9wAGRF5Dq3z433dhOaMlmGnLi5EBD8f+5BbQIrzn0tcmhjQgatuAuQw8SZaTqtOEnaQpNvyrOPjpcldED2XbIV1jZYqLZn8Y9/PBc8WKARlctmsuXqiBYzOb6hXweK9DwIDAQAB  # 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问  notify_url: http://05h0g893.dongtaiyuming.net/notify_url  # 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问  # return_url: https://api.itbooking.net/admin/alipay/returnUrl  return_url: http://05h0g893.dongtaiyuming.net/return_url  # 签名方式  sign_type: RSA2  # 字符编码格式  charset: utf-8  # 支付宝网关  gatewayUrl: https://openapi.alipaydev.com/gateway.do  # 保存支付日志的地址  log_path: c:/tmp/

3.2.2、配置类

@Component@Data@PropertySource(value = "classpath:application.yml")public class PayAliConfig {    // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号    @Value("${alipay.app_id}")    public  String app_id;    // 商户私钥,您的PKCS8格式RSA2私钥    @Value("${alipay.merchant_private_key}")    public String merchant_private_key;    // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。    @Value("${alipay.alipay_public_key}")    public String alipay_public_key;    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问    @Value("${alipay.notify_url}")    public  String notify_url;    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问    @Value("${alipay.return_url}")    public  String return_url;    // 签名方式    @Value("${alipay.sign_type}")    public  String sign_type;    // 字符编码格式    @Value("${alipay.charset}")    public  String charset;    // 支付宝网关    @Value("${alipay.gatewayUrl}")    public String gatewayUrl;    // 日志存放    @Value("${alipay.log_path}")    public String log_path;}

3.3、支付业务实现

3.3.1、获取支付码

请求参数
参数 类型 是否必填 最大长度 描述 示例值
out_trade_no String 必选 64 商户订单号。 由商家自定义,64个字符以内,仅支持字母、数字、下划线且需保证在商户端不重复。 20150320010101001
total_amount Price 必选 11 订单总金额,单位为元,精确到小数点后两位,取值范围为 [0.01,100000000]。金额不能为0。 88.88
subject String 必选 256 订单标题。 注意:不可使用特殊字符,如 /,=,& 等。 Iphone6 16G
product_code String 必选 64 销售产品码,与支付宝签约的产品码名称。注:目前电脑支付场景下仅支持FAST_INSTANT_TRADE_PAY FAST_INSTANT_TRADE_PAY
qr_pay_mode String 可选 2 PC扫码支付的方式。 支持前置模式和跳转模式。 前置模式是将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以 iframe 方式请求支付宝页面。具体支持的枚举值有以下几种: 0:订单码-简约前置模式,对应 iframe 宽度不能小于600px,高度不能小于300px; 1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于600px; 3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于75px; 4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。 跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。支持传入的枚举值有: 2:订单码-跳转模式 1
qrcode_width Number 可选 4 商户自定义二维码宽度。 注:qr_pay_mode=4时该参数有效 100
goods_detail GoodsDetail[] 可选 订单包含的商品列表信息,json格式。
time_expire String 可选 32 订单绝对超时时间。 格式为yyyy-MM-dd HH:mm:ss。 注:time_expire和timeout_express两者只需传入一个或者都不传,两者均传入时,优先使用time_expire。 2016-12-31 10:05:01
sub_merchant SubMerchant 可选 二级商户信息。 直付通模式和机构间连模式下必传,其它场景下不需要传入。
extend_params ExtendParams 可选 业务扩展参数
business_params String 可选 512 商户传入业务信息,具体值要和支付宝约定,应用于安全,营销等参数直传场景,格式为json格式 {“data”:“123”}
promo_params String 可选 512 优惠参数。为 JSON 格式。注:仅与支付宝协商后可用 {“storeIdType”:“1”}
integration_type String 可选 16 请求后页面的集成方式。 枚举值: ALIAPP:支付宝钱包内 PCWEB:PC端访问 默认值为PCWEB。 PCWEB
request_from_url String 可选 256 请求来源地址。如果使用ALIAPP的集成方式,用户中途取消支付会返回该地址。 https://
store_id String 可选 32 商户门店编号。 指商户创建门店时输入的门店编号。 NJ_001
merchant_order_no String 可选 32 商户原始订单号,最大长度限制 32 位 20161008001
invoice_info InvoiceInfo 可选 开票信息

支付宝依赖中提供了参数模板类AlipayTradePrecreateModel可用改类镜像参数封装

AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();     model.setOutTradeNo("2022112223227"); // 自定义订单号     model.setTotalAmount("111");// 支付金额     model.setSubject("凯迪拉克");// 支付的产品名称     model.setBody(params);// 支付的请求体参数     model.setTimeoutExpress("30m");// 支付的超时时间     model.setStoreId("111"+"");// 支付的库存id

其中body中的数据用户可以自定义商品的支付信息为了之后便于解析,一般为json格式,在支付回调时后,支付平台会将改参数返回,可根据该参数更新本地数据。

  • JSONObject json = new JSONObject();json.put("userId", "111");json.put("orderNumber", "2022112223227");json.put("money", 111);json.put("ptype","productcourse");// 支付类型json.put("courseId","3");String params = json.toString();

参数准备完成

客户端封装

支付宝提供了专用的请求客户端AlipayClient

AlipayClient alipayClient = new DefaultAlipayClient(    payAliConfig.getGatewayUrl(),  //配置类中的支付宝网关    payAliConfig.getApp_id(),      //配置类中的支付宝appid    payAliConfig.getMerchant_private_key(), //配置类中的支付宝秘钥  "JSON",     //参数类型    payAliConfig.getCharset(),     //字符编码    payAliConfig.getAlipay_public_key(),   //配置类中的支付宝公钥    payAliConfig.getSign_type());   //签名方式
请求封装

支付宝提供了专用的请求对象AlipayTradePrecreateRequest

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();// 3:设置请求参数的集合,最大长度不限request.setBizModel(model);  //将上面封装的数据,绑定在请求中// 设置异步回调地址request.setNotifyUrl(payAliConfig.getNotify_url());// 设置同步回调地址request.setReturnUrl(payAliConfig.getReturn_url());
发送请求
 alipayClient.execute(request);
返回响应

支付宝提供了专用的响应对象AlipayTradePrecreateResponse支付宝返回的响应中,body中封装了业务数据,为了方便解析返回的的数据,我们可以封装一个响应对象AliResponse

public class AliResponse {    private QrCodeResponse alipay_trade_precreate_response;    private String sign;    public QrCodeResponse getAlipay_trade_precreate_response() { return alipay_trade_precreate_response;    }    public void setAlipay_trade_precreate_response(QrCodeResponse alipay_trade_precreate_response) { this.alipay_trade_precreate_response = alipay_trade_precreate_response;    }    public String getSign() { return sign;    }    public void setSign(String sign) { this.sign = sign;    }}

QrCodeResponse

public class QrCodeResponse {    /**     * 返回的状态码     */    private String code;    /**     * 返回的信息     */    private String msg;    /**     * 交易的流水号     */    private String out_trade_no;    /**     * 生成二维码的内容     */    private String qr_code;    public String getCode() { return code;    }    public void setCode(String code) { this.code = code;    }    public String getMsg() { return msg;    }    public void setMsg(String msg) { this.msg = msg;    }    public String getOut_trade_no() { return out_trade_no;    }    public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no;    }    public String getQr_code() { return qr_code;    }    public void setQr_code(String qr_code) { this.qr_code = qr_code;    }    @Override    public String toString() { return "QrCodeResponse{" +  "code='" + code + '\'' +  ", msg='" + msg + '\'' +  ", out_trade_no='" + out_trade_no + '\'' +  ", qr_code='" + qr_code + '\'' +  '}';    }}

其中qr_code便是二维码生成链接

参数解析
AliResponse aliResponse = JSON.parseObject(alipayTradePrecreateResponse.getBody(), AliResponse.class);return aliResponse.getAlipay_trade_precreate_response();//将QrCodeResponse返回到前端便可

3.3.2、支付码生成

最简单的做法可以利用vue插件在前端生成

<div>    <qriously:value="payObj.qr_code":size="220"/>    <div style="text-align: center;line-height: 25px;margin-bottom: 40px;"> 请使用支付宝扫一扫<br> 扫描二维码支付    </div></div>
效果展示:

支付宝支付(详细版)

3.3.3、扫码支付,结果回调

下载支付宝沙箱版登陆之后,扫码支付

支付宝支付(详细版)

点击立即支付后,支付宝预支付成功后,会调用回调地址,将预支付结果、以及支付信息返回,可以判断预支付成功后,解析支付信息参数,更新本地支付记录、订单数据校验,返回success,否则支付平台会接着重试。

支付结果解析

支付宝回调返回的参数

AliPayDto(gmt_create=[2022-05-01 17:06:58], charset=[utf-8], seller_email=[towpcq0063@sandbox.com], subject=[凯迪拉克], sign=[NQYGTZBoqHj+7vEu4EGSUOd8/ixOsn2Pj247srOi23lvldV3RdcJJ3qczYPkr4U8z0Yan7Uv+Pb54c+D7k/6d9XI35V3v8RYx1pCqp3nvF/0bBjPKzLpESaxxRwF82FnGtPU+0herGQjxRZbPNrtfJ9FeuIRVKqs0XhR/TMNwMl+TZLwlnOdyada87UW658igkNkT0GYfxSossgb4v3Rr3FSo0sL605jnvivJMbjo+6Jw+9HvYuYMx63tTt109Q==], body=[{ "orderNumber":"2022112223227", "money":111, "ptype":"productcourse", "userId":"111", "courseId":"3" }],  buyer_id=[2088622956330370],  invoice_amount=[111.00],  notify_id=[2022050100222170709030370521355515],  fund_bill_list=[[{"amount":"111.00","fundChannel":"ALIPAYACCOUNT"}]],  notify_type=[trade_status_sync],  trade_status=[TRADE_SUCCESS],  receipt_amount=[111.00],  buyer_pay_amount=[111.00],  app_id=[2021000117697224],  sign_type=[RSA2],  seller_id=[2088621956217164],  gmt_payment=[2022-05-01 17:07:08],  notify_time=[2022-05-01 17:07:09],  version=[1.0],  out_trade_no=[2022112223227],  total_amount=[111.00],  trade_no=[2022050122001430370502275683],  auth_app_id=[2021000117697224],  buyer_logon_id=[lwx***@sandbox.com],  point_amount=[0.00])

支付信息是map格式,可以使用FastJson解析成自己封装的对象。

Map<String, String[]> parameterMap = request.getParameterMap();String string = JSON.toJSONString(parameterMap);AliPayDto aliPayDto = JSON.parseObject(string, AliPayDto.class);

自己封装的对象

@Datapublic class AliPayDto {    private String[] gmt_create;    private String[] charset;    private String[] seller_email;    private String[] subject;    private String[] sign;    private String[] body;    private String[] buyer_id;    private String[] invoice_amount;    private String[] notify_id;    private String[] fund_bill_list;    private String[] notify_type;    private String[] trade_status;    private String[] receipt_amount;    private String[] buyer_pay_amount;    private String[] app_id;    private String[] sign_type;    private String[] seller_id;    private String[] gmt_payment;    private String[] notify_time;    private String[] version;    private String[] out_trade_no;    private String[] total_amount;    private String[] trade_no;    private String[] auth_app_id;    private String[] buyer_logon_id;    private String[] point_amount;}

解析结果:

AliPayDto(gmt_create=[2022-05-02 12:48:22], charset=[utf-8], seller_email=[towpcq0063@sandbox.com], subject=[凯迪拉克], sign=[IUs73oHiL4STH4/Q928H+YSMCH9pNdoAVeNVM4X2XONaGyXJD3eRJP9lYVJmgQtutV4RFyvKkTKXaQfs9+XGkPQbjkrVkOps61B3raT+wNzuur6+/o6jOWa526L/l65T2Ix4oH5+TPjk0D4zXT4NXcFek5uqnkt54g2kYO9HNRhRIMg1sQVN7KtyVuOuk3o1oCxgJnO8OCwZEzmNRRJ/m6xlhYwQ801izIK1Q/kC3GZhuftpbEY616KV5NXIJfbDndQZxTzRFYIi70H7ovTtJNjGqO+qSj932N+PRZYvrTPe1h0oC5HebDV23sUTvABOzXvdlFZi025XWRkbtwxpsQ==], body=[{"orderNumber":"2022112223229","money":111,"ptype":"productcourse","userId":"111","courseId":"3"}], buyer_id=[2088622956330370], invoice_amount=[111.00], notify_id=[2022050200222124834030370521366673], fund_bill_list=[[{"amount":"111.00","fundChannel":"ALIPAYACCOUNT"}]], notify_type=[trade_status_sync], trade_status=[TRADE_SUCCESS], receipt_amount=[111.00], buyer_pay_amount=[111.00], app_id=[2021000117697224], sign_type=[RSA2], seller_id=[2088621956217164], gmt_payment=[2022-05-02 12:48:33], notify_time=[2022-05-02 12:48:34], version=[1.0], out_trade_no=[2022112223229], total_amount=[111.00], trade_no=[2022050222001430370502275841], auth_app_id=[2021000117697224], buyer_logon_id=[lwx***@sandbox.com], point_amount=[0.00])
后续操作:
  • 根据解析结果返回的数据更新支付记录、回写订单数据

  • (1)前端定时任务查询订单数据,回写成功后跳转页面。

    该方式会在短时间内发起大量请求,造成一定的服务器资源开销

    (2)集成webSoket让服务器主动向前端推送支付结果。

闲鱼礼物网