支付宝支付(详细版)
支付宝支付
一、准备
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让服务器主动向前端推送支付结果。