Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jinli gu
Jeepay
Commits
21814d03
Commit
21814d03
authored
Nov 29, 2021
by
大森林
Committed by
Gitee
Nov 29, 2021
Browse files
!6 feta: 添加 Paypal 渠道支持
Merge pull request !6 from 青木/paypal
parents
53e7b0f0
a4e26945
Changes
19
Hide whitespace changes
Inline
Side-by-side
docs/sql/init.sql
View file @
21814d03
...
...
@@ -718,6 +718,8 @@ INSERT INTO t_pay_way (way_code, way_name) VALUES ('WX_LITE', '微信小程序')
INSERT
INTO
t_pay_way
(
way_code
,
way_name
)
VALUES
(
'YSF_BAR'
,
'云闪付条码'
);
INSERT
INTO
t_pay_way
(
way_code
,
way_name
)
VALUES
(
'YSF_JSAPI'
,
'云闪付jsapi'
);
INSERT
INTO
t_pay_way
(
way_code
,
way_name
)
VALUES
(
'PP_PC'
,
'Paypal PC 支付'
);
-- 初始化支付接口定义
INSERT
INTO
t_pay_interface_define
(
if_code
,
if_name
,
is_mch_mode
,
is_isv_mode
,
config_page_type
,
isv_params
,
isvsub_mch_params
,
normal_mch_params
,
way_codes
,
icon
,
bg_color
,
state
,
remark
)
VALUES
(
'alipay'
,
'支付宝官方'
,
1
,
1
,
2
,
...
...
@@ -742,3 +744,11 @@ VALUES ('ysfpay', '云闪付官方', 0, 1, 1,
NULL
,
'[{"wayCode": "YSF_BAR"}, {"wayCode": "ALI_JSAPI"}, {"wayCode": "WX_JSAPI"}, {"wayCode": "ALI_BAR"}, {"wayCode": "WX_BAR"}]'
,
'http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/ysfpay.png'
,
'red'
,
1
,
'云闪付官方通道'
);
INSERT
INTO
t_pay_interface_define
(
if_code
,
if_name
,
is_mch_mode
,
is_isv_mode
,
config_page_type
,
isv_params
,
isvsub_mch_params
,
normal_mch_params
,
way_codes
,
icon
,
bg_color
,
state
,
remark
)
VALUES
(
'pppay'
,
'Paypal 支付'
,
1
,
0
,
1
,
NULL
,
NULL
,
'[{"name":"sandbox","desc":"环境配置","type":"radio","verify":"required","values":"1,0","titles":"沙箱环境, 生产环境"},{"name":"clientId","desc":"Client ID","type":"text","verify":"required"},{"name":"secret","desc":"Secret","type":"text","verify":"required"},{"name":"refundWebhook","desc":"退款 Webhook id","type":"text","verify":"required"},{"name":"notifyWebhook","desc":"通知 Webhook id","type":"text","verify":"required"}]'
,
'[{"wayCode": "PP_PC"}]'
,
'https://payment-public.oss-cn-shenzhen.aliyuncs.com/ifBG/0b6c2cc3-d31b-4f5c-b076-f13c74d80b85.png'
,
'#005ea6'
,
1
,
'Paypal官方通道'
);
jeepay-core/src/main/java/com/jeequan/jeepay/core/constants/CS.java
View file @
21814d03
...
...
@@ -144,6 +144,7 @@ public class CS {
String
WXPAY
=
"wxpay"
;
// 微信官方支付
String
YSFPAY
=
"ysfpay"
;
// 云闪付开放平台
String
XXPAY
=
"xxpay"
;
// 小新支付
String
PPPAY
=
"pppay"
;
// Paypal 支付
}
...
...
@@ -169,6 +170,8 @@ public class CS {
String
WX_BAR
=
"WX_BAR"
;
//微信条码支付
String
WX_H5
=
"WX_H5"
;
//微信H5支付
String
WX_NATIVE
=
"WX_NATIVE"
;
//微信扫码支付
String
PP_PC
=
"PP_PC"
;
// Paypal 支付
}
//支付数据包 类型
...
...
jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/NormalMchParams.java
View file @
21814d03
...
...
@@ -18,6 +18,7 @@ package com.jeequan.jeepay.core.model.params;
import
com.alibaba.fastjson.JSONObject
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.model.params.alipay.AlipayNormalMchParams
;
import
com.jeequan.jeepay.core.model.params.pppay.PpPayNormalMchParams
;
import
com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams
;
import
com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams
;
...
...
@@ -38,6 +39,8 @@ public abstract class NormalMchParams {
return
JSONObject
.
parseObject
(
paramsStr
,
AlipayNormalMchParams
.
class
);
}
else
if
(
CS
.
IF_CODE
.
XXPAY
.
equals
(
ifCode
)){
return
JSONObject
.
parseObject
(
paramsStr
,
XxpayNormalMchParams
.
class
);
}
else
if
(
CS
.
IF_CODE
.
PPPAY
.
equals
(
ifCode
)){
return
JSONObject
.
parseObject
(
paramsStr
,
PpPayNormalMchParams
.
class
);
}
return
null
;
}
...
...
jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/pppay/PpPayNormalMchParams.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.core.model.params.pppay
;
import
com.alibaba.fastjson.JSON
;
import
com.alibaba.fastjson.JSONObject
;
import
com.jeequan.jeepay.core.model.params.NormalMchParams
;
import
com.jeequan.jeepay.core.utils.StringKit
;
import
lombok.Data
;
import
org.apache.commons.lang3.StringUtils
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.core.model.params.pppay
* @create 2021/11/15 18:10
*/
@Data
public
class
PpPayNormalMchParams
extends
NormalMchParams
{
/**
* 是否沙箱环境
*/
private
Byte
sandbox
;
/**
* clientId
* 客户端 ID
*/
private
String
clientId
;
/**
* secret
* 密钥
*/
private
String
secret
;
/**
* 支付 Webhook 通知 ID
*/
private
String
notifyWebhook
;
/**
* 退款 Webhook 通知 ID
*/
private
String
refundWebhook
;
@Override
public
String
deSenData
()
{
PpPayNormalMchParams
mchParams
=
this
;
if
(
StringUtils
.
isNotBlank
(
this
.
secret
))
{
mchParams
.
setSecret
(
StringKit
.
str2Star
(
this
.
secret
,
6
,
6
,
6
));
}
return
((
JSONObject
)
JSON
.
toJSON
(
mchParams
)).
toJSONString
();
}
}
jeepay-payment/pom.xml
View file @
21814d03
...
...
@@ -105,6 +105,13 @@
<artifactId>
alipay-sdk-java
</artifactId>
</dependency>
<!-- paypal 支付 -->
<dependency>
<groupId>
com.paypal.sdk
</groupId>
<artifactId>
checkout-sdk
</artifactId>
<version>
1.0.5
</version>
</dependency>
</dependencies>
...
...
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/AbstractPaymentService.java
View file @
21814d03
...
...
@@ -58,4 +58,8 @@ public abstract class AbstractPaymentService implements IPaymentService{
return
sysConfigService
.
getDBApplicationConfig
().
getPaySiteUrl
()
+
"/api/pay/return/"
+
getIfCode
();
}
protected
String
getReturnUrl
(
String
payOrderId
){
return
sysConfigService
.
getDBApplicationConfig
().
getPaySiteUrl
()
+
"/api/pay/return/"
+
getIfCode
()
+
"/"
+
payOrderId
;
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/PppayChannelNoticeService.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.core.exception.ResponseException
;
import
com.jeequan.jeepay.pay.channel.AbstractChannelNoticeService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.tuple.MutablePair
;
import
org.springframework.stereotype.Service
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.IOException
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay
* @create 2021/11/15 20:58
*/
@Service
@Slf4j
public
class
PppayChannelNoticeService
extends
AbstractChannelNoticeService
{
@Override
public
String
getIfCode
()
{
return
CS
.
IF_CODE
.
PPPAY
;
}
@Override
public
MutablePair
<
String
,
Object
>
parseParams
(
HttpServletRequest
request
,
String
urlOrderId
,
NoticeTypeEnum
noticeTypeEnum
)
{
// 同步和异步需要不同的解析方案
// 异步需要从 webhook 中读取,所有这里读取方式不太一样
if
(
noticeTypeEnum
==
NoticeTypeEnum
.
DO_NOTIFY
)
{
JSONObject
params
=
JSONUtil
.
parseObj
(
getReqParamJSON
().
toJSONString
());
String
orderId
=
params
.
getByPath
(
"resource.purchase_units[0].invoice_id"
,
String
.
class
);
return
MutablePair
.
of
(
orderId
,
params
);
}
else
{
if
(
urlOrderId
==
null
||
urlOrderId
.
isEmpty
())
{
throw
ResponseException
.
buildText
(
"ERROR"
);
}
try
{
JSONObject
params
=
JSONUtil
.
parseObj
(
getReqParamJSON
().
toString
());
return
MutablePair
.
of
(
urlOrderId
,
params
);
}
catch
(
Exception
e
)
{
log
.
error
(
"error"
,
e
);
throw
ResponseException
.
buildText
(
"ERROR"
);
}
}
}
@Override
public
ChannelRetMsg
doNotice
(
HttpServletRequest
request
,
Object
params
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
,
NoticeTypeEnum
noticeTypeEnum
)
{
try
{
if
(
noticeTypeEnum
==
NoticeTypeEnum
.
DO_RETURN
)
{
return
doReturn
(
request
,
params
,
payOrder
,
mchAppConfigContext
);
}
return
doNotify
(
request
,
params
,
payOrder
,
mchAppConfigContext
);
}
catch
(
Exception
e
)
{
log
.
error
(
"error"
,
e
);
throw
ResponseException
.
buildText
(
"ERROR"
);
}
}
public
ChannelRetMsg
doReturn
(
HttpServletRequest
request
,
Object
params
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
IOException
{
JSONObject
object
=
(
JSONObject
)
params
;
// 获取 Paypal 订单 ID
String
ppOrderId
=
object
.
getStr
(
"token"
);
// 统一处理订单
return
mchAppConfigContext
.
getPaypalWrapper
().
processOrder
(
ppOrderId
,
payOrder
);
}
public
ChannelRetMsg
doNotify
(
HttpServletRequest
request
,
Object
params
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
IOException
{
JSONObject
object
=
(
JSONObject
)
params
;
// 获取 Paypal 订单 ID
String
ppOrderId
=
object
.
getByPath
(
"resource.id"
,
String
.
class
);
// 统一处理订单
return
mchAppConfigContext
.
getPaypalWrapper
().
processOrder
(
ppOrderId
,
payOrder
,
true
);
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/PppayChannelRefundNoticeService.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.entity.RefundOrder
;
import
com.jeequan.jeepay.core.exception.ResponseException
;
import
com.jeequan.jeepay.pay.channel.AbstractChannelRefundNoticeService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.model.PaypalWrapper
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
com.paypal.core.PayPalHttpClient
;
import
com.paypal.http.HttpResponse
;
import
com.paypal.http.serializer.Json
;
import
com.paypal.payments.Refund
;
import
com.paypal.payments.RefundsGetRequest
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.tuple.MutablePair
;
import
org.springframework.stereotype.Service
;
import
javax.servlet.http.HttpServletRequest
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay
* @create 2021/11/16 20:39
*/
@Service
@Slf4j
public
class
PppayChannelRefundNoticeService
extends
AbstractChannelRefundNoticeService
{
@Override
public
String
getIfCode
()
{
return
CS
.
IF_CODE
.
PPPAY
;
}
@Override
public
MutablePair
<
String
,
Object
>
parseParams
(
HttpServletRequest
request
,
String
urlOrderId
,
NoticeTypeEnum
noticeTypeEnum
)
{
JSONObject
params
=
JSONUtil
.
parseObj
(
getReqParamJSON
().
toJSONString
());
// 获取退款订单 Paypal ID
String
orderId
=
params
.
getByPath
(
"resource.invoice_id"
,
String
.
class
);
return
MutablePair
.
of
(
orderId
,
params
);
}
@Override
public
ChannelRetMsg
doNotice
(
HttpServletRequest
request
,
Object
params
,
RefundOrder
refundOrder
,
MchAppConfigContext
mchAppConfigContext
,
NoticeTypeEnum
noticeTypeEnum
)
{
try
{
JSONObject
object
=
(
JSONObject
)
params
;
String
orderId
=
object
.
getByPath
(
"resource.id"
,
String
.
class
);
PaypalWrapper
wrapper
=
mchAppConfigContext
.
getPaypalWrapper
();
PayPalHttpClient
client
=
wrapper
.
getClient
();
// 查询退款详情以及状态
RefundsGetRequest
refundRequest
=
new
RefundsGetRequest
(
orderId
);
HttpResponse
<
Refund
>
response
=
client
.
execute
(
refundRequest
);
ChannelRetMsg
channelRetMsg
=
ChannelRetMsg
.
waiting
();
channelRetMsg
.
setResponseEntity
(
wrapper
.
textResp
(
"ERROR"
));
if
(
response
.
statusCode
()
==
200
)
{
String
responseJson
=
new
Json
().
serialize
(
response
.
result
());
channelRetMsg
=
wrapper
.
dispatchCode
(
response
.
result
().
status
(),
channelRetMsg
);
channelRetMsg
.
setChannelAttach
(
responseJson
);
channelRetMsg
.
setChannelOrderId
(
response
.
result
().
id
());
channelRetMsg
.
setResponseEntity
(
wrapper
.
textResp
(
"SUCCESS"
));
}
else
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_FAIL
);
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"异步退款失败,Paypal 响应非 200"
);
}
return
channelRetMsg
;
}
catch
(
Exception
e
)
{
log
.
error
(
"error"
,
e
);
throw
ResponseException
.
buildText
(
"ERROR"
);
}
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/PppayPayOrderQueryService.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.pay.channel.IPayOrderQueryService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
org.springframework.stereotype.Service
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay
* @create 2021/11/15 21:02
*/
@Service
public
class
PppayPayOrderQueryService
implements
IPayOrderQueryService
{
@Override
public
String
getIfCode
()
{
return
CS
.
IF_CODE
.
PPPAY
;
}
@Override
public
ChannelRetMsg
query
(
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
Exception
{
return
mchAppConfigContext
.
getPaypalWrapper
().
processOrder
(
null
,
payOrder
);
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/PppayPaymentService.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.pay.channel.AbstractPaymentService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.rqrs.AbstractRS
;
import
com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ
;
import
com.jeequan.jeepay.pay.util.PaywayUtil
;
import
org.springframework.stereotype.Service
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay
* @create 2021/11/15 18:17
*/
@Service
public
class
PppayPaymentService
extends
AbstractPaymentService
{
@Override
public
String
getIfCode
()
{
return
CS
.
IF_CODE
.
PPPAY
;
}
@Override
public
boolean
isSupport
(
String
wayCode
)
{
return
true
;
}
@Override
public
String
preCheck
(
UnifiedOrderRQ
bizRQ
,
PayOrder
payOrder
)
{
return
PaywayUtil
.
getRealPaywayService
(
this
,
payOrder
.
getWayCode
()).
preCheck
(
bizRQ
,
payOrder
);
}
@Override
public
AbstractRS
pay
(
UnifiedOrderRQ
bizRQ
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
Exception
{
return
PaywayUtil
.
getRealPaywayService
(
this
,
payOrder
.
getWayCode
()).
pay
(
bizRQ
,
payOrder
,
mchAppConfigContext
);
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/PppayRefundService.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.core.entity.RefundOrder
;
import
com.jeequan.jeepay.pay.channel.AbstractRefundService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.model.PaypalWrapper
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRQ
;
import
com.paypal.core.PayPalHttpClient
;
import
com.paypal.http.HttpResponse
;
import
com.paypal.http.serializer.Json
;
import
com.paypal.payments.*
;
import
org.springframework.stereotype.Service
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay
* @create 2021/11/16 20:20
*/
@Service
public
class
PppayRefundService
extends
AbstractRefundService
{
@Override
public
String
getIfCode
()
{
return
CS
.
IF_CODE
.
PPPAY
;
}
@Override
public
String
preCheck
(
RefundOrderRQ
bizRQ
,
RefundOrder
refundOrder
,
PayOrder
payOrder
)
{
return
null
;
}
@Override
public
ChannelRetMsg
refund
(
RefundOrderRQ
bizRQ
,
RefundOrder
refundOrder
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
Exception
{
if
(
payOrder
.
getChannelOrderNo
()
==
null
)
{
return
ChannelRetMsg
.
confirmFail
();
}
PaypalWrapper
paypalWrapper
=
mchAppConfigContext
.
getPaypalWrapper
();
// 因为退款需要商户 Token 而同步支付回调不会保存订单信息
String
ppOrderId
=
paypalWrapper
.
processOrder
(
payOrder
.
getChannelOrderNo
()).
get
(
0
);
String
ppCatptId
=
paypalWrapper
.
processOrder
(
payOrder
.
getChannelOrderNo
()).
get
(
1
);
if
(
ppOrderId
==
null
||
ppCatptId
==
null
)
{
return
ChannelRetMsg
.
confirmFail
();
}
PayPalHttpClient
client
=
paypalWrapper
.
getClient
();
// 处理金额
long
amount
=
(
bizRQ
.
getRefundAmount
()
/
100
);
String
amountStr
=
Long
.
toString
(
amount
,
10
);
String
currency
=
bizRQ
.
getCurrency
().
toUpperCase
();
RefundRequest
refundRequest
=
new
RefundRequest
();
Money
money
=
new
Money
();
money
.
currencyCode
(
currency
);
money
.
value
(
amountStr
);
refundRequest
.
invoiceId
(
refundOrder
.
getRefundOrderId
());
refundRequest
.
amount
(
money
);
refundRequest
.
noteToPayer
(
bizRQ
.
getRefundReason
());
CapturesRefundRequest
request
=
new
CapturesRefundRequest
(
ppCatptId
);
request
.
prefer
(
"return=representation"
);
request
.
requestBody
(
refundRequest
);
HttpResponse
<
Refund
>
response
=
client
.
execute
(
request
);
ChannelRetMsg
channelRetMsg
=
ChannelRetMsg
.
waiting
();
channelRetMsg
.
setResponseEntity
(
paypalWrapper
.
textResp
(
"ERROR"
));
if
(
response
.
statusCode
()
==
201
)
{
String
responseJson
=
new
Json
().
serialize
(
response
.
result
());
channelRetMsg
=
paypalWrapper
.
dispatchCode
(
response
.
result
().
status
(),
channelRetMsg
);
channelRetMsg
.
setChannelAttach
(
responseJson
);
channelRetMsg
.
setChannelOrderId
(
response
.
result
().
id
());
channelRetMsg
.
setResponseEntity
(
paypalWrapper
.
textResp
(
"SUCCESS"
));
}
else
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_FAIL
);
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"请求退款失败,Paypal 响应非 201"
);
}
return
channelRetMsg
;
}
@Override
public
ChannelRetMsg
query
(
RefundOrder
refundOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
Exception
{
if
(
refundOrder
.
getChannelOrderNo
()
==
null
)
{
return
ChannelRetMsg
.
confirmFail
();
}
PaypalWrapper
wrapper
=
mchAppConfigContext
.
getPaypalWrapper
();
PayPalHttpClient
client
=
wrapper
.
getClient
();
RefundsGetRequest
refundRequest
=
new
RefundsGetRequest
(
refundOrder
.
getPayOrderId
());
HttpResponse
<
Refund
>
response
=
client
.
execute
(
refundRequest
);
ChannelRetMsg
channelRetMsg
=
ChannelRetMsg
.
waiting
();
channelRetMsg
.
setResponseEntity
(
wrapper
.
textResp
(
"ERROR"
));
if
(
response
.
statusCode
()
==
201
)
{
String
responseJson
=
new
Json
().
serialize
(
response
.
result
());
channelRetMsg
=
wrapper
.
dispatchCode
(
response
.
result
().
status
(),
channelRetMsg
);
channelRetMsg
.
setChannelAttach
(
responseJson
);
channelRetMsg
.
setChannelOrderId
(
response
.
result
().
id
());
channelRetMsg
.
setResponseEntity
(
wrapper
.
textResp
(
"SUCCESS"
));
}
else
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_FAIL
);
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"请求退款详情失败,Paypal 响应非 200"
);
}
return
channelRetMsg
;
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/pppay/payway/PpPc.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.channel.pppay.payway
;
import
cn.hutool.json.JSONUtil
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.core.exception.BizException
;
import
com.jeequan.jeepay.pay.channel.pppay.PppayPaymentService
;
import
com.jeequan.jeepay.pay.model.MchAppConfigContext
;
import
com.jeequan.jeepay.pay.model.PaypalWrapper
;
import
com.jeequan.jeepay.pay.rqrs.AbstractRS
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ
;
import
com.jeequan.jeepay.pay.rqrs.payorder.payway.PPPcOrderRQ
;
import
com.jeequan.jeepay.pay.rqrs.payorder.payway.PPPcOrderRS
;
import
com.paypal.http.HttpResponse
;
import
com.paypal.http.serializer.Json
;
import
com.paypal.orders.*
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.channel.pppay.payway
* @create 2021/11/15 18:59
*/
@Slf4j
@Service
(
"pppayPaymentByPPPCService"
)
public
class
PpPc
extends
PppayPaymentService
{
@Override
public
String
preCheck
(
UnifiedOrderRQ
bizRQ
,
PayOrder
payOrder
)
{
PPPcOrderRQ
rq
=
(
PPPcOrderRQ
)
bizRQ
;
if
(
StringUtils
.
isEmpty
(
rq
.
getCancelUrl
()))
{
throw
new
BizException
(
"用户取消支付回调[cancelUrl]不可为空"
);
}
return
null
;
}
@Override
public
AbstractRS
pay
(
UnifiedOrderRQ
rq
,
PayOrder
payOrder
,
MchAppConfigContext
mchAppConfigContext
)
throws
Exception
{
PPPcOrderRQ
bizRQ
=
(
PPPcOrderRQ
)
rq
;
OrderRequest
orderRequest
=
new
OrderRequest
();
// 配置 Paypal ApplicationContext 也就是支付页面信息
ApplicationContext
applicationContext
=
new
ApplicationContext
()
.
brandName
(
mchAppConfigContext
.
getMchApp
().
getAppName
())
.
landingPage
(
"NO_PREFERENCE"
)
.
cancelUrl
(
bizRQ
.
getCancelUrl
())
.
returnUrl
(
getReturnUrl
(
payOrder
.
getPayOrderId
()))
.
userAction
(
"PAY_NOW"
)
.
shippingPreference
(
"NO_SHIPPING"
);
orderRequest
.
applicationContext
(
applicationContext
);
orderRequest
.
checkoutPaymentIntent
(
"CAPTURE"
);
List
<
PurchaseUnitRequest
>
purchaseUnitRequests
=
new
ArrayList
<>();
// 金额换算
long
amount
=
(
payOrder
.
getAmount
()
/
100
);
String
amountStr
=
Long
.
toString
(
amount
,
10
);
String
currency
=
payOrder
.
getCurrency
().
toUpperCase
();
// 由于 Paypal 是支持订单多商品的,这里值添加一个
PurchaseUnitRequest
purchaseUnitRequest
=
new
PurchaseUnitRequest
()
// 绑定 订单 ID 否则回调和异步较难处理
.
customId
(
payOrder
.
getPayOrderId
())
.
invoiceId
(
payOrder
.
getPayOrderId
())
.
amountWithBreakdown
(
new
AmountWithBreakdown
()
.
currencyCode
(
currency
)
.
value
(
amountStr
)
.
amountBreakdown
(
new
AmountBreakdown
().
itemTotal
(
new
Money
().
currencyCode
(
currency
).
value
(
amountStr
))
)
)
.
items
(
new
ArrayList
<
Item
>()
{
{
add
(
new
Item
()
.
name
(
payOrder
.
getSubject
())
.
description
(
payOrder
.
getBody
())
.
sku
(
payOrder
.
getPayOrderId
())
.
unitAmount
(
new
Money
().
currencyCode
(
currency
).
value
(
amountStr
))
.
quantity
(
"1"
)
);
}
});
purchaseUnitRequests
.
add
(
purchaseUnitRequest
);
orderRequest
.
purchaseUnits
(
purchaseUnitRequests
);
// 从缓存获取 Paypal 操作工具
PaypalWrapper
palApiConfig
=
mchAppConfigContext
.
getPaypalWrapper
();
OrdersCreateRequest
request
=
new
OrdersCreateRequest
();
request
.
header
(
"prefer"
,
"return=representation"
);
request
.
requestBody
(
orderRequest
);
HttpResponse
<
Order
>
response
=
palApiConfig
.
getClient
().
execute
(
request
);
PPPcOrderRS
res
=
new
PPPcOrderRS
();
ChannelRetMsg
channelRetMsg
=
new
ChannelRetMsg
();
// 标准返回 HttpPost 需要为 201
if
(
response
.
statusCode
()
==
201
)
{
Order
order
=
response
.
result
();
String
status
=
response
.
result
().
status
();
String
tradeNo
=
response
.
result
().
id
();
// 从返回数据里读取出支付链接
LinkDescription
paypalLink
=
order
.
links
().
stream
().
reduce
(
null
,
(
result
,
curr
)
->
{
if
(
curr
.
rel
().
equalsIgnoreCase
(
"approve"
)
&&
curr
.
method
().
equalsIgnoreCase
(
"get"
))
{
result
=
curr
;
}
return
result
;
});
// 设置返回实体
channelRetMsg
.
setChannelAttach
(
JSONUtil
.
toJsonStr
(
new
Json
().
serialize
(
order
)));
channelRetMsg
.
setChannelOrderId
(
tradeNo
+
","
+
"null"
);
// 拼接订单ID
channelRetMsg
=
palApiConfig
.
dispatchCode
(
status
,
channelRetMsg
);
// 处理状态码
// 设置支付链接
res
.
setPayUrl
(
paypalLink
.
href
());
}
else
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_FAIL
);
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"请求失败,Paypal 响应非 201"
);
}
res
.
setChannelRetMsg
(
channelRetMsg
);
return
res
;
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/ctrl/payorder/ChannelNoticeController.java
View file @
21814d03
...
...
@@ -81,7 +81,7 @@ public class ChannelNoticeController extends AbstractCtrl {
}
// 解析订单号 和 请求参数
MutablePair
<
String
,
Object
>
mutablePair
=
payNotifyService
.
parseParams
(
request
,
urlOrderId
,
IChannelNoticeService
.
NoticeTypeEnum
.
DO_
NOTIFY
);
MutablePair
<
String
,
Object
>
mutablePair
=
payNotifyService
.
parseParams
(
request
,
urlOrderId
,
IChannelNoticeService
.
NoticeTypeEnum
.
DO_
RETURN
);
if
(
mutablePair
==
null
){
// 解析数据失败, 响应已处理
log
.
error
(
"{}, mutablePair is null "
,
logPrefix
);
throw
new
BizException
(
"解析数据异常!"
);
//需要实现类自行抛出ResponseException, 不应该在这抛此异常。
...
...
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/model/MchAppConfigContext.java
View file @
21814d03
...
...
@@ -50,6 +50,8 @@ public class MchAppConfigContext {
/** 放置所属服务商的信息 **/
private
IsvConfigContext
isvConfigContext
;
/** 缓存 Paypal 对象 **/
private
PaypalWrapper
paypalWrapper
;
/** 缓存支付宝client 对象 **/
private
AlipayClientWrapper
alipayClientWrapper
;
...
...
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/model/PaypalWrapper.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.model
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.jeequan.jeepay.core.entity.PayOrder
;
import
com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg
;
import
com.paypal.core.PayPalEnvironment
;
import
com.paypal.core.PayPalHttpClient
;
import
com.paypal.http.HttpResponse
;
import
com.paypal.http.serializer.Json
;
import
com.paypal.orders.*
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.ResponseEntity
;
import
java.io.IOException
;
import
java.util.Arrays
;
import
java.util.List
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.model
* @create 2021/11/15 19:10
*/
@Slf4j
@Data
@AllArgsConstructor
@NoArgsConstructor
public
class
PaypalWrapper
{
private
PayPalEnvironment
environment
;
private
PayPalHttpClient
client
;
private
String
notifyWebhook
;
private
String
refundWebhook
;
public
ChannelRetMsg
processOrder
(
String
token
,
PayOrder
payOrder
)
throws
IOException
{
return
processOrder
(
token
,
payOrder
,
false
);
}
public
List
<
String
>
processOrder
(
String
order
)
{
return
processOrder
(
order
,
"null"
);
}
// 解析拼接 ID
public
List
<
String
>
processOrder
(
String
order
,
String
afterOrderId
)
{
String
ppOrderId
=
"null"
;
String
ppCatptId
=
"null"
;
if
(
order
!=
null
)
{
if
(
order
.
contains
(
","
))
{
String
[]
split
=
order
.
split
(
","
);
if
(
split
.
length
==
2
)
{
ppCatptId
=
split
[
1
];
ppOrderId
=
split
[
0
];
}
}
}
if
(
afterOrderId
!=
null
&&
!
"null"
.
equalsIgnoreCase
(
afterOrderId
))
{
ppOrderId
=
afterOrderId
;
}
if
(
"null"
.
equalsIgnoreCase
(
ppCatptId
))
{
ppCatptId
=
null
;
}
if
(
"null"
.
equalsIgnoreCase
(
ppOrderId
))
{
ppOrderId
=
null
;
}
return
Arrays
.
asList
(
ppOrderId
,
ppCatptId
);
}
/**
* 处理并捕获订单
* 由于 Paypal 创建订单后需要进行一次 Capture(捕获) 才可以正确获取到订单的支付状态
*
* @param token
* @param payOrder
* @param isCapture
* @return
* @throws IOException
*/
public
ChannelRetMsg
processOrder
(
String
token
,
PayOrder
payOrder
,
boolean
isCapture
)
throws
IOException
{
// Paypal 创建订单存在一个 Token,当订单捕获之后会有一个 捕获的ID ,退款需要用到
String
ppOrderId
=
this
.
processOrder
(
payOrder
.
getChannelOrderNo
(),
token
).
get
(
0
);
String
ppCatptId
=
this
.
processOrder
(
payOrder
.
getChannelOrderNo
()).
get
(
1
);
ChannelRetMsg
channelRetMsg
=
ChannelRetMsg
.
waiting
();
channelRetMsg
.
setResponseEntity
(
textResp
(
"ERROR"
));
// 如果订单 ID 还不存在,等待
if
(
ppOrderId
==
null
)
{
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"捕获订单请求失败"
);
return
channelRetMsg
;
}
else
{
Order
order
;
channelRetMsg
.
setChannelOrderId
(
ppOrderId
+
","
+
"null"
);
// 如果 捕获 ID 不存在
if
(
ppCatptId
==
null
&&
isCapture
)
{
OrderRequest
orderRequest
=
new
OrderRequest
();
OrdersCaptureRequest
ordersCaptureRequest
=
new
OrdersCaptureRequest
(
ppOrderId
);
ordersCaptureRequest
.
requestBody
(
orderRequest
);
// 捕获订单
HttpResponse
<
Order
>
response
=
this
.
getClient
().
execute
(
ordersCaptureRequest
);
if
(
response
.
statusCode
()
!=
201
)
{
channelRetMsg
.
setChannelErrCode
(
"201"
);
channelRetMsg
.
setChannelErrMsg
(
"捕获订单请求失败"
);
return
channelRetMsg
;
}
order
=
response
.
result
();
}
else
{
OrdersGetRequest
request
=
new
OrdersGetRequest
(
ppOrderId
);
HttpResponse
<
Order
>
response
=
this
.
getClient
().
execute
(
request
);
if
(
response
.
statusCode
()
!=
200
)
{
channelRetMsg
.
setChannelOrderId
(
ppOrderId
);
channelRetMsg
.
setChannelErrCode
(
"200"
);
channelRetMsg
.
setChannelErrMsg
(
"请求订单详情失败"
);
return
channelRetMsg
;
}
order
=
response
.
result
();
}
String
status
=
order
.
status
();
String
orderJsonStr
=
new
Json
().
serialize
(
order
);
JSONObject
orderJson
=
JSONUtil
.
parseObj
(
orderJsonStr
);
for
(
PurchaseUnit
purchaseUnit
:
order
.
purchaseUnits
())
{
if
(
purchaseUnit
.
payments
()
!=
null
)
{
for
(
Capture
capture
:
purchaseUnit
.
payments
().
captures
())
{
ppCatptId
=
capture
.
id
();
break
;
}
}
}
String
orderUserId
=
orderJson
.
getByPath
(
"payer.payer_id"
,
String
.
class
);
ChannelRetMsg
result
=
new
ChannelRetMsg
();
result
.
setNeedQuery
(
true
);
result
.
setChannelOrderId
(
ppOrderId
+
","
+
ppCatptId
);
// 渠道订单号
result
.
setChannelUserId
(
orderUserId
);
// 支付用户ID
result
.
setChannelAttach
(
orderJsonStr
);
// Capture 响应数据
result
.
setResponseEntity
(
textResp
(
"SUCCESS"
));
// 响应数据
result
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
WAITING
);
// 默认支付中
result
=
dispatchCode
(
status
,
result
);
// 处理状态码
return
result
;
}
}
/**
* 处理 Paypal 状态码
*
* @param status 状态码
* @param channelRetMsg 通知信息
* @return 通知信息
*/
public
ChannelRetMsg
dispatchCode
(
String
status
,
ChannelRetMsg
channelRetMsg
)
{
if
(
"SAVED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
WAITING
);
}
else
if
(
"APPROVED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
WAITING
);
}
else
if
(
"VOIDED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_FAIL
);
}
else
if
(
"COMPLETED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
CONFIRM_SUCCESS
);
}
else
if
(
"PAYER_ACTION_REQUIRED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
WAITING
);
}
else
if
(
"CREATED"
.
equalsIgnoreCase
(
status
))
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
WAITING
);
}
else
{
channelRetMsg
.
setChannelState
(
ChannelRetMsg
.
ChannelState
.
UNKNOWN
);
}
return
channelRetMsg
;
}
public
ResponseEntity
textResp
(
String
text
)
{
HttpHeaders
httpHeaders
=
new
HttpHeaders
();
httpHeaders
.
setContentType
(
MediaType
.
TEXT_HTML
);
return
new
ResponseEntity
(
text
,
httpHeaders
,
HttpStatus
.
OK
);
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/UnifiedOrderRQ.java
View file @
21814d03
...
...
@@ -146,6 +146,10 @@ public class UnifiedOrderRQ extends AbstractMchAppRQ {
AliQrOrderRQ
bizRQ
=
JSONObject
.
parseObject
(
StringUtils
.
defaultIfEmpty
(
this
.
channelExtra
,
"{}"
),
AliQrOrderRQ
.
class
);
BeanUtils
.
copyProperties
(
this
,
bizRQ
);
return
bizRQ
;
}
else
if
(
CS
.
PAY_WAY_CODE
.
PP_PC
.
equals
(
wayCode
)){
PPPcOrderRQ
bizRQ
=
JSONObject
.
parseObject
(
StringUtils
.
defaultIfEmpty
(
this
.
channelExtra
,
"{}"
),
PPPcOrderRQ
.
class
);
BeanUtils
.
copyProperties
(
this
,
bizRQ
);
return
bizRQ
;
}
return
this
;
...
...
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/PPPcOrderRQ.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.rqrs.payorder.payway
;
import
com.jeequan.jeepay.core.constants.CS
;
import
com.jeequan.jeepay.pay.rqrs.payorder.CommonPayDataRQ
;
import
lombok.Data
;
import
javax.validation.constraints.NotBlank
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.rqrs.payorder.payway
* @create 2021/11/15 17:52
*/
@Data
public
class
PPPcOrderRQ
extends
CommonPayDataRQ
{
/**
* 商品描述信息
**/
@NotBlank
(
message
=
"取消支付返回站点"
)
private
String
cancelUrl
;
public
PPPcOrderRQ
()
{
this
.
setWayCode
(
CS
.
PAY_WAY_CODE
.
PP_PC
);
}
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/PPPcOrderRS.java
0 → 100644
View file @
21814d03
package
com.jeequan.jeepay.pay.rqrs.payorder.payway
;
import
com.jeequan.jeepay.pay.rqrs.payorder.CommonPayDataRS
;
import
lombok.Data
;
/**
* none.
*
* @author 陈泉
* @package com.jeequan.jeepay.pay.rqrs.payorder.payway
* @create 2021/11/15 19:56
*/
@Data
public
class
PPPcOrderRS
extends
CommonPayDataRS
{
}
jeepay-payment/src/main/java/com/jeequan/jeepay/pay/service/ConfigContextService.java
View file @
21814d03
...
...
@@ -25,10 +25,13 @@ import com.jeequan.jeepay.core.model.params.IsvsubMchParams;
import
com.jeequan.jeepay.core.model.params.NormalMchParams
;
import
com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams
;
import
com.jeequan.jeepay.core.model.params.alipay.AlipayNormalMchParams
;
import
com.jeequan.jeepay.core.model.params.pppay.PpPayNormalMchParams
;
import
com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvParams
;
import
com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams
;
import
com.jeequan.jeepay.pay.model.*
;
import
com.jeequan.jeepay.service.impl.*
;
import
com.paypal.core.PayPalEnvironment
;
import
com.paypal.core.PayPalHttpClient
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
...
...
@@ -223,6 +226,12 @@ public class ConfigContextService {
mchAppConfigContext
.
setWxServiceWrapper
(
WxServiceWrapper
.
buildWxServiceWrapper
(
wxpayParams
));
}
//放置 paypal client
PpPayNormalMchParams
ppPayMchParams
=
mchAppConfigContext
.
getNormalMchParamsByIfCode
(
CS
.
IF_CODE
.
PPPAY
,
PpPayNormalMchParams
.
class
);
if
(
ppPayMchParams
!=
null
)
{
mchAppConfigContext
.
setPaypalWrapper
(
buildPaypalWrapper
(
ppPayMchParams
.
getSandbox
(),
ppPayMchParams
.
getSecret
(),
ppPayMchParams
.
getClientId
(),
ppPayMchParams
.
getNotifyWebhook
(),
ppPayMchParams
.
getRefundWebhook
()));
}
}
else
{
//服务商模式商户
for
(
PayInterfaceConfig
payInterfaceConfig
:
allConfigList
)
{
...
...
@@ -317,6 +326,28 @@ public class ConfigContextService {
}
}
private
PaypalWrapper
buildPaypalWrapper
(
Byte
sandbox
,
String
secret
,
String
clientId
,
String
notifyHook
,
String
refundHook
)
{
PaypalWrapper
paypalWrapper
=
new
PaypalWrapper
();
PayPalEnvironment
environment
=
new
PayPalEnvironment
.
Live
(
clientId
,
secret
);
if
(
sandbox
==
1
)
{
environment
=
new
PayPalEnvironment
.
Sandbox
(
clientId
,
secret
);
}
paypalWrapper
.
setEnvironment
(
environment
);
paypalWrapper
.
setClient
(
new
PayPalHttpClient
(
environment
));
paypalWrapper
.
setNotifyWebhook
(
notifyHook
);
paypalWrapper
.
setRefundWebhook
(
refundHook
);
return
paypalWrapper
;
}
private
boolean
isCache
(){
return
SysConfigService
.
IS_USE_CACHE
;
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment