實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

一:簡介

手機網站支付常用於HTML5應用,常見於微信公眾號上的應用。手機網站支付文檔

手機網站支付的流程圖:

  1. 用戶點擊H5應用中的支付按鈕
  2. 點擊支付按鈕會請求後臺接口,後臺接口請求支付寶的支付接口,支付接口會返回一段html代碼其中包括一個form表單和一段js代碼用於自動提交表單,表單提交後就會自動跳轉到支付寶的支付頁面(如果手機中裝了支付App就去打開APP,如果沒有就在網頁版支付
  3. 支付成功後會調用支付時設置的同步url, 然後跳轉到商戶的後臺系統,一般情況下商戶系統會展示一下支付成功,以及購買的商品信息等視圖

流程圖

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

二:集成步驟

0. 創建應用、配置密鑰

集成前需要先創建應用、配置密鑰、回調地址等,具體操作請查看Spring Boot入門教程(三十五):支付寶集成-準備工作

1. dependency

<code>

<

dependency

>

<

groupId

>

org.springframework.boot

groupId

>

<

artifactId

>

spring-boot-starter-web

artifactId

>

dependency

>

<

dependency

>

<

groupId

>

org.springframework.boot

groupId

>

<

artifactId

>

spring-boot-starter-test

artifactId

>

<

scope

>

test

scope

>

dependency

>

<

dependency

>

<

groupId

>

org.springframework.boot

groupId

>

<

artifactId

>

spring-boot-starter-thymeleaf

artifactId

>

dependency

>

<

dependency

>

<

groupId

>

com.alipay

groupId

>

<

artifactId

>

alipay-sdk-java

artifactId

>

<

version

>

20170725114550

version

>

dependency

>

<

dependency

>

<

groupId

>

com.alipay

groupId

>

<

artifactId

>

alipay-trade-sdk

artifactId

>

<

version

>

20161215

version

>

dependency

>

<

dependency

>

<

groupId

>

com.google.code.gson

groupId

>

<

artifactId

>

gson

artifactId

>

dependency

>

<

dependency

>

<

groupId

>

commons-configuration

groupId

>

<

artifactId

>

commons-configuration

artifactId

>

<

version

>

1.10

version

>

dependency

>

<

dependency

>

<

groupId

>

com.google.zxing

groupId

>

<

artifactId

>

core

artifactId

>

<

version

>

3.2.1

version

>

dependency

>

<

dependency

>

<

groupId

>

org.projectlombok

groupId

>

<

artifactId

>

lombok

artifactId

>

dependency

>

/<code>

2. application.yml

手機網站支付需要支付成功後同步的地址returnUrl,支付成功後跳轉到的頁面,這個值既可以配在這裡也可以在Java代碼調用支付寶接口時也可以設置為別的值

<code> 

pay:

alipay:

gatewayUrl:

https://openapi.alipaydev.com/gateway.do

appid:

2016091400508498

appPrivateKey:

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtXKWs+trRSuCxEUvlsEeSAuLWs3B/Dh74R8223BnfzoA29aGeoycAqfKlBMcbzU2G1KayESvZKGpwBLeejSbecRYjgZsQDyEaDimQQJtGFvZVV6u4XNnvIJ72eQzEaEIQfuiorjBTLm6DQuds4R0GxftqON6QFoIZkWB9ZrZKd02cuy16dW+UqtLVGGAHcCIAkB63zUiKSNfweMddneZ7MVs3lvu3xhMnD+5us/+n2Vp4qhfmpYLcdqIW6InU4GypeoOpyUTrfUGpgdR0l924vHy/GQJZEKFaRcK3cYK+ECyKpSIoqaJJFLHbkqsliuPpMUG+rM3jiqeIAH4psAznAgMBAAECggEBAJ5jyEbbxrJzrAh7GhHX1fwUQPYSadTbrPYAfHX2cHlnrQMJtsk+nTLhEv0r+VJwZ8WpYkfMong8kcqYtL7ajcmsHqMAFhE9EWxBxj2ymWsXLabZe93sj30IG9Rq0nxcGQgDO0RqKWLGSFgK93Al2KRInKT3InkY53K+vR61ihVLmGf7+GwPtIZteBy+tuAyvcj2RlkYvjiFi3ySyZ1wA3sJIlgrGiixd6fj20XFGNE3CnAwu0BJgXXbP/S9J4C0RRa3ZXj8fX7oONhVxz2xKxn7AT4e8OWjt7J41H2LRct8Fgl9pqgz2FJYv/WfbkG8x9fGiKYYvPXoxjjrk/tkewkCgYEA8f9Lcu5JPrE9rpw9zlwhm5cOO81xLxdwL5R5/1bRP48BZGIYuqlCbVvjJVqtO8eTnLhUwH7fG8B7cmoeO9bGr9GQrtfyCqz6FtVymTBieJlfgZDVhtzyv2qKOBMIFE8jsbSBK/NHHMvykJ+XdQ1riwCeQDdXICRuYTTFwGk2OsUCgYEAt2SoN95tVmVrvKG6ATLNEtge/ozeVywA4GjltrSw/G9vqp+DkkT2pY19uROuzMazoTzKWpPho2q/qzNlv/ANbOFM2GEmKamQ7CO88JgRxMsPTvc/HxCLU/ClMJU8LcOf9LfP2KYZpPwuheKJoF4vDGj8NsbFmccJyYSdpkNEk7sCgYBJlL2FMaz1sgC2Ue19DIhvfaunRV1P20mSPgwmNmijccETm7w3LXX0OIdFeV/JGHLqqSWj7i+6iXk/ncKZoUGCfi8G6sQ+uL/GJ5qTt6GJV+ExTS+PtSjeSO/EAw1m13Vb+C16hpstx1l23f+4aJ81gbecgPct38XsKpaiXZtOnQKBgQCMsN7QRYYxwoq9YoDUzIlAzKYyeBVWYL6najHYUZR5hG/xQIBqZRem9/4cTvpJxKInrwA6LrrqaEl0aHDFp75U6h7O3PCvA5PXZK9dD/yJsZIj7U/yX/nTQokn1UUegrYiwiTkusBvrrtuINWePsLvTVc4GpObHnPmsiNTWsWwYwKBgENaeTNOCHV2km/ysXQSEIhKbtlAMQPsgWHCt/bzHlF9m18izb1LrJyjzcSsd+Zy78R+pv4G50Q27c3e/DFPz/wYxN/yHWRbyLBA8ipJbCtMtPEdS9krpmN6cChIdLGbz4CVUqOPSRzNb9lhhgPCcCNRq6DG3HBceb1Se9VnO3zk

alipayPublicKey:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFQKccMq+wPoWh93DcX3XYrnT7FJ3gntJA/jEwgk6Jd3iEVS2CyUCCgFVcQn8xjXT81YbZHYvoC50IBuu+A+Ey+J8VIgsBm5g9uwbOLRf66GrZjuKOlalHm5gHXjcL2gZRMBJEStOxstcO2YQriqhQzdL3EKp+HQc9u14IOVFpZdR8qq1l7CzKHn1vQo/1fUPfUrTLQqSuQTU9Ospv/QHFqOJA7DPetUQ+jnaZ082f3clr4ITw4EE8A6IWqTXcETTx5j/udCGP84g2Y4j+8i9DqYGyD5ePVgt4G0ICBX1bi1qNlylfxRg8Y3c1DFrRGyr0NpKQxSVXkYaVNvrCoudQIDAQAB

returnUrl:

http://yxep7y.natappfree.cc/alipay/return

notifyUrl:

http://yxep7y.natappfree.cc/alipay/notify

spring:

thymeleaf:

prefix:

classpath:/templates/

suffix:

.html

mode:

HTML5

encoding:

UTF-8

/<code>

3.AlipayProperties

<code> 
 
 (prefix = 

"pay.alipay"

)

public

class

AlipayProperties {

private

String

gatewayUrl;

private

String

appid;

private

String

appPrivateKey;

private

String

alipayPublicKey;

private

String

signType =

"RSA2"

;

private

String

formate =

"json"

;

private

String

charset =

"UTF-8"

;

private

String

returnUrl;

private

String

notifyUrl;

private

static

int maxQueryRetry =

5

;

private

static

long queryDuration =

5000

;

private

static

int maxCancelRetry =

3

;

private

static

long cancelDuration =

3000

;

private

AlipayProperties() {}

public

void

init() { log.info(description()); }

public

String

description() { StringBuilder sb =

new

StringBuilder(

"\nConfigs{"

); sb.append(

"支付寶網關: "

).append(gatewayUrl).append(

"\n"

); sb.append(

", appid: "

).append(appid).append(

"\n"

); sb.append(

", 商戶RSA私鑰: "

).append(getKeyDescription(appPrivateKey)).append(

"\n"

); sb.append(

", 支付寶RSA公鑰: "

).append(getKeyDescription(alipayPublicKey)).append(

"\n"

); sb.append(

", 簽名類型: "

).append(signType).append(

"\n"

); sb.append(

", 查詢重試次數: "

).append(maxQueryRetry).append(

"\n"

); sb.append(

", 查詢間隔(毫秒): "

).append(queryDuration).append(

"\n"

); sb.append(

", 撤銷嘗試次數: "

).append(maxCancelRetry).append(

"\n"

); sb.append(

", 撤銷重試間隔(毫秒): "

).append(cancelDuration).append(

"\n"

); sb.append(

"}"

);

return

sb.toString(); }

private

String

getKeyDescription(

String

key) { int showLength =

6

;

if

(StringUtils.isNotEmpty(key) && key.length() > showLength) {

return

new

StringBuilder(key.substring(

0

, showLength)).append(

"******"

) .append(key.substring(key.length() - showLength)).toString(); }

return

null

; } } /<code>

4.AlipayConfiguration

<code>

@Configuration

@EnableConfigurationProperties(AlipayProperties.class) public class AlipayConfiguration {

@Autowired

private AlipayProperties properties;

@Bean

public AlipayTradeService alipayTradeService() {

return

new

AlipayTradeServiceImpl

.ClientBuilder

()

.setGatewayUrl

(

properties

.getGatewayUrl

())

.setAppid

(

properties

.getAppid

())

.setPrivateKey

(

properties

.getAppPrivateKey

())

.setAlipayPublicKey

(

properties

.getAlipayPublicKey

())

.setSignType

(

properties

.getSignType

())

.build

(); }

@Bean

public AlipayClient alipayClient(){

return

new

DefaultAlipayClient

(

properties

.getGatewayUrl

(),

properties

.getAppid

(),

properties

.getAppPrivateKey

(),

properties

.getFormate

(),

properties

.getCharset

(),

properties

.getAlipayPublicKey

(),

properties

.getSignType

()); } } /<code>

5. AlipayWAPPayController

<code> 
 4j
 
 (

"/alipay/wap"

)

public

class

AlipayWAPPayController

{

private

AlipayProperties alipayProperties;

private

AlipayClient alipayClient; (

"/alipage"

)

public

void

gotoPayPage

(HttpServletResponse response)

throws

AlipayApiException, IOException

{ String productCode=

"QUICK_WAP_WAY"

; AlipayTradeWapPayModel model =

new

AlipayTradeWapPayModel(); model.setOutTradeNo(UUID.randomUUID().toString()); model.setSubject(

"支付測試"

); model.setTotalAmount(

"0.01"

); model.setBody(

"支付測試,共0.01元"

); model.setTimeoutExpress(

"2m"

); model.setProductCode(productCode); AlipayTradeWapPayRequest wapPayRequest =

new

AlipayTradeWapPayRequest(); wapPayRequest.setReturnUrl(

"http://yxep7y.natappfree.cc/alipay/wap/returnUrl"

); wapPayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); wapPayRequest.setBizModel(model); String form = alipayClient.pageExecute(wapPayRequest).getBody(); System.out.println(form); response.setContentType(

"text/html;charset="

+ alipayProperties.getCharset()); response.getWriter().write(form); response.getWriter().flush(); response.getWriter().close(); } (

"/returnUrl"

)

public

String

returnUrl

(HttpServletRequest request, HttpServletResponse response)

throws

UnsupportedEncodingException, AlipayApiException

{ response.setContentType(

"text/html;charset="

+ alipayProperties.getCharset()); Map params =

new

HashMap<>(); Map requestParams = request.getParameterMap();

for

(Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) 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); }

boolean

verifyResult = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(), alipayProperties.getCharset(),

"RSA2"

);

if

(verifyResult){ String out_trade_no =

new

String(request.getParameter(

"out_trade_no"

).getBytes(

"ISO-8859-1"

),

"UTF-8"

); String trade_no =

new

String(request.getParameter(

"trade_no"

).getBytes(

"ISO-8859-1"

),

"UTF-8"

);

return

"wapPaySuccess"

; }

else

{

return

"wapPayFail"

; } } (

"/refund"

)

public

String

refund

(String orderNo)

throws

AlipayApiException

{ AlipayTradeRefundRequest alipayRequest =

new

AlipayTradeRefundRequest(); AlipayTradeRefundModel model=

new

AlipayTradeRefundModel(); model.setOutTradeNo(orderNo); model.setRefundAmount(

"0.01"

); model.setRefundReason(

"無理由退貨"

); alipayRequest.setBizModel(model); AlipayTradeRefundResponse alipayResponse = alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody());

return

alipayResponse.getBody(); } (

"/refundQuery"

)

public

String

refundQuery

(String orderNo, String refundOrderNo)

throws

AlipayApiException

{ AlipayTradeFastpayRefundQueryRequest alipayRequest =

new

AlipayTradeFastpayRefundQueryRequest(); AlipayTradeFastpayRefundQueryModel model=

new

AlipayTradeFastpayRefundQueryModel(); model.setOutTradeNo(orderNo); model.setOutRequestNo(refundOrderNo); alipayRequest.setBizModel(model); AlipayTradeFastpayRefundQueryResponse alipayResponse = alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody());

return

alipayResponse.getBody(); } (

"/close"

)

public

String

close

(String orderNo)

throws

AlipayApiException

{ AlipayTradeCloseRequest alipayRequest =

new

AlipayTradeCloseRequest(); AlipayTradeCloseModel model =

new

AlipayTradeCloseModel(); model.setOutTradeNo(orderNo); alipayRequest.setBizModel(model); AlipayTradeCloseResponse alipayResponse= alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody());

return

alipayResponse.getBody(); } } /<code>

6. AlipayController

<code> 
 
 
 (

"/alipay"

)

public

class

AlipayController {

private

AlipayProperties aliPayProperties;

private

AlipayTradeService alipayTradeService; (

"/notify"

)

public

String

notify(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException { Map<

String

,

String

[]> parameterMap = request.getParameterMap(); StringBuilder notifyBuild =

new

StringBuilder(

"/****************************** alipay notify ******************************/\n"

); parameterMap.forEach((key, value) -> notifyBuild.append(key +

"="

+ value[

0

] +

"\n"

) ); log.info(notifyBuild.toString()); Map<

String

,

String

> params =

new

HashMap<>(); Map requestParams = request.getParameterMap();

for

(Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {

String

name = (

String

) iter.next();

String

[] values = (

String

[]) requestParams.get(name);

String

valueStr =

""

;

for

(int i =

0

; i < values.length; i++) { valueStr = (i == values.length -

1

) ? valueStr + values[i] : valueStr + values[i] +

","

; } params.put(name, valueStr); }

boolean

flag = AlipaySignature.rsaCheckV1(params, aliPayProperties.getAlipayPublicKey(), aliPayProperties.getCharset(), aliPayProperties.getSignType());

if

(flag) {

String

tradeStatus =

new

String

(request.getParameter(

"trade_status"

).getBytes(

"ISO-8859-1"

),

"UTF-8"

);

if

(tradeStatus.equals(

"TRADE_FINISHED"

)){ }

else

if

(tradeStatus.equals(

"TRADE_SUCCESS"

)){ }

return

"success"

; }

return

"fail"

; } (

"/query"

)

public

String

query(

String

orderNo){ AlipayTradeQueryRequestBuilder builder =

new

AlipayTradeQueryRequestBuilder() .setOutTradeNo(orderNo); AlipayF2FQueryResult result = alipayTradeService.queryTradeResult(builder);

switch

(result.getTradeStatus()) {

case

SUCCESS: log.info(

"查詢返回該訂單支付成功: )"

); AlipayTradeQueryResponse resp = result.getResponse(); log.info(resp.getTradeStatus());

break

;

case

FAILED: log.error(

"查詢返回該訂單支付失敗!!!"

);

break

;

case

UNKNOWN: log.error(

"系統異常,訂單支付狀態未知!!!"

);

break

;

default

: log.error(

"不支持的交易狀態,交易返回異常!!!"

);

break

; }

return

result.getResponse().getBody(); } } /<code>

7. WebMvcConfiguration

通過訪問
http://localhost:8080/toPay來跳轉到toPay.html頁面

<code> 

public

class

WebMvcConfiguration

extends

WebMvcConfigurationSupport

{

protected

void

addViewControllers

(ViewControllerRegistry registry)

{ registry.addViewController(

"/toPay"

).setViewName(

"toPay"

);

super

.addViewControllers(registry); } } /<code>

8. templates

toPay.html

<code> >

<

html

lang

=

"en"

>

<

head

>

<

meta

charset

=

"UTF-8"

>

<

title

>

Title

title

>

head

>

<

body

style

=

"font-size: 30px"

>

<

form

method

=

"post"

action

=

"/alipay/wap/alipage"

>

<

h3

>

購買商品:越南新娘

h3

>

<

h3

>

價格:20000

h3

>

<

h3

>

數量:2個

h3

>

<

button

style

=

"width: 100%; height: 60px; alignment: center; background: blue"

type

=

"submit"

>

支付

button

>

form

>

body

>

html

>

/<code>

wapPaySuccess.html

<code> >

<

html

lang

=

"en"

>

<

head

>

<

meta

charset

=

"UTF-8"

>

<

title

>

Title

title

>

head

>

<

body

>

<

h1

>

WAP 支付成功,請及時享用!歡迎下次再來

h1

>

body

>

html

>

/<code>

wapPayFail.html

<code> >

<

html

lang

=

"en"

>

<

head

>

<

meta

charset

=

"UTF-8"

>

<

title

>

Title

title

>

head

>

<

body

>

<

h2

>

WAP 支付失敗,請重新支付

h2

>

body

>

html

>

/<code>

三:運行結果

首先訪問去支付頁面:
http://localhost:8080/toPay

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

實用乾貨 | 一步一步教你在SpringBoot集成支付寶手機網站支付

獲取源碼

關注並私信“支付寶手機網站支付”獲取源代碼。


分享到:


相關文章: