11package com .egzosn .pay .paypal .v2 .api ;
22
33
4+ import java .io .IOException ;
45import java .io .InputStream ;
56import java .io .UnsupportedEncodingException ;
7+ import java .security .cert .X509Certificate ;
68import java .util .ArrayList ;
9+ import java .util .Collection ;
710import java .util .Collections ;
811import java .util .Date ;
12+ import java .util .Enumeration ;
913import java .util .HashMap ;
10- import java .util .LinkedHashMap ;
1114import java .util .List ;
1215import java .util .Map ;
1316import java .util .UUID ;
2326import com .egzosn .pay .common .api .BasePayService ;
2427import com .egzosn .pay .common .bean .AssistOrder ;
2528import com .egzosn .pay .common .bean .BillType ;
26-
2729import com .egzosn .pay .common .bean .CurType ;
2830import com .egzosn .pay .common .bean .DefaultCurType ;
2931import com .egzosn .pay .common .bean .MethodType ;
3032import com .egzosn .pay .common .bean .NoticeParams ;
33+ import com .egzosn .pay .common .bean .NoticeRequest ;
3134import com .egzosn .pay .common .bean .PayMessage ;
3235import com .egzosn .pay .common .bean .PayOrder ;
3336import com .egzosn .pay .common .bean .PayOutMessage ;
3841import com .egzosn .pay .common .exception .PayErrorException ;
3942import com .egzosn .pay .common .http .HttpHeader ;
4043import com .egzosn .pay .common .http .HttpStringEntity ;
44+ import com .egzosn .pay .common .http .ResponseEntity ;
4145import com .egzosn .pay .common .http .UriVariables ;
46+ import com .egzosn .pay .common .util .IOUtils ;
4247import com .egzosn .pay .common .util .Util ;
4348import com .egzosn .pay .common .util .str .StringUtils ;
4449import com .egzosn .pay .paypal .api .PayPalConfigStorage ;
50+ import com .egzosn .pay .paypal .v2 .bean .Constants ;
4551import com .egzosn .pay .paypal .v2 .bean .PayPalRefundResult ;
4652import com .egzosn .pay .paypal .v2 .bean .PayPalTransactionType ;
4753import com .egzosn .pay .paypal .v2 .bean .order .ApplicationContext ;
4854import com .egzosn .pay .paypal .v2 .bean .order .Money ;
4955import com .egzosn .pay .paypal .v2 .bean .order .OrderRequest ;
5056import com .egzosn .pay .paypal .v2 .bean .order .PurchaseUnitRequest ;
5157import com .egzosn .pay .paypal .v2 .bean .order .ShippingDetail ;
58+ import com .egzosn .pay .paypal .v2 .utils .PayPalUtil ;
5259
5360
5461/**
6168 */
6269public class PayPalPayService extends BasePayService <PayPalConfigStorage > implements PayPalPayServiceInf {
6370
71+
6472 /**
6573 * 沙箱环境
6674 */
@@ -161,12 +169,18 @@ public String getAccessToken(boolean forceRefresh) throws PayErrorException {
161169 @ Deprecated
162170 @ Override
163171 public boolean verify (Map <String , Object > params ) {
164- return verify (new NoticeParams (params ));
172+
173+ throw new PayErrorException (new PayException ("failure" , "payPal V2版本不支持此校验方式" ));
165174
166175 }
167176
168- @ Override
169- public boolean verify (NoticeParams noticeParams ) {
177+
178+ /**
179+ * 保留IPN的校验方式
180+ * @param noticeParams 参数
181+ * @return 结果
182+ */
183+ public boolean verifyIpn (NoticeParams noticeParams ) {
170184 final Map <String , Object > params = noticeParams .getBody ();
171185 Object paymentStatus = params .get ("payment_status" );
172186 if (!"Completed" .equals (paymentStatus )) {
@@ -177,6 +191,64 @@ public boolean verify(NoticeParams noticeParams) {
177191 return "VERIFIED" .equals (resp );
178192
179193 }
194+ @ Override
195+ public boolean verify (NoticeParams noticeParams ) {
196+
197+ final Map <String , List <String >> headers = noticeParams .getHeaders ();
198+ if (null == headers || headers .isEmpty ()) {
199+ throw new PayErrorException (new PayException ("failure" , "校验失败,请求头不能为空" ));
200+ }
201+
202+
203+ String clientCertificateLocation = noticeParams .getHeader (Constants .PAYPAL_HEADER_CERT_URL );
204+ ResponseEntity <InputStream > clientCertificateResponseEntity = requestTemplate .getForObjectEntity (clientCertificateLocation , InputStream .class );
205+ if (clientCertificateResponseEntity .getStatusCode () > 400 ) {
206+ LOG .error ("获取证书信息失败,无法进行webHook校验:{}" , clientCertificateLocation );
207+ return false ;
208+ }
209+ InputStream inputStream = clientCertificateResponseEntity .getBody ();
210+ Collection <X509Certificate > clientCerts = PayPalUtil .getCertificateFromStream (inputStream );
211+ Map <String , Object > body = noticeParams .getBody ();
212+ String webHookId = (String ) body .get (Constants .ID );
213+ String actualSignatureEncoded = noticeParams .getHeader (Constants .PAYPAL_HEADER_TRANSMISSION_SIG );
214+ String authAlgo = noticeParams .getHeader (Constants .PAYPAL_HEADER_AUTH_ALGO );
215+ String transmissionId = noticeParams .getHeader (Constants .PAYPAL_HEADER_TRANSMISSION_ID );
216+ String transmissionTime = noticeParams .getHeader (Constants .PAYPAL_HEADER_TRANSMISSION_TIME );
217+ String requestBody = noticeParams .getBodyStr ();
218+ String expectedSignature = String .format ("%s|%s|%s|%s" , transmissionId , transmissionTime , webHookId , PayPalUtil .crc32 (requestBody ));
219+ boolean isDataValid = PayPalUtil .validateData (clientCerts , authAlgo , actualSignatureEncoded , expectedSignature );
220+ LOG .debug ("数据校验结果: {}" , isDataValid );
221+ return isDataValid ;
222+
223+ }
224+
225+ /**
226+ * 将请求参数或者请求流转化为 Map
227+ *
228+ * @param request 通知请求
229+ * @return 获得回调的请求参数
230+ */
231+ @ Override
232+ public NoticeParams getNoticeParams (NoticeRequest request ) {
233+ NoticeParams noticeParams = new NoticeParams ();
234+ try (InputStream is = request .getInputStream ()) {
235+ String body = IOUtils .toString (is );
236+ noticeParams .setBodyStr (body );
237+ noticeParams .setBody (JSON .parseObject (body ));
238+ }
239+ catch (IOException e ) {
240+ throw new PayErrorException (new PayException ("failure" , "获取回调参数异常" ), e );
241+ }
242+ Map <String , List <String >> headers = new HashMap <>();
243+ Enumeration <String > headerNames = request .getHeaderNames ();
244+ while (headerNames .hasMoreElements ()) {
245+ String name = headerNames .nextElement ();
246+ headers .put (name , Collections .list (request .getHeaders (name )));
247+ }
248+ noticeParams .setHeaders (headers );
249+ return noticeParams ;
250+ }
251+
180252 /**
181253 * 获取授权请求头
182254 *
@@ -305,14 +377,14 @@ public Map<String, Object> orderInfo(PayOrder order) {
305377
306378 @ Override
307379 public PayOutMessage getPayOutMessage (String code , String message ) {
308- String out = "The response from IPN was: <b>" + code + "</b>" ;
309- return PayOutMessage .TEXT ().content (out ).build ();
380+
381+ return PayOutMessage .TEXT ().content (code ).build ();
310382 }
311383
312384 @ Override
313385 public PayOutMessage successPayOutMessage (PayMessage payMessage ) {
314- Map < String , Object > message = payMessage . getPayMessage ();
315- return new PayPalOutMessageBuilder ( message ).build ();
386+
387+ return PayOutMessage . TEXT (). content ( "200" ).build ();
316388 }
317389
318390 @ Override
@@ -367,16 +439,18 @@ public Map<String, Object> query(AssistOrder assistOrder) {
367439 public Map <String , Object > close (String tradeNo , String outTradeNo ) {
368440 return null ;
369441 }
442+
370443 /**
371444 * 交易关闭接口
372445 *
373- * @param assistOrder 关闭订单
446+ * @param assistOrder 关闭订单
374447 * @return 返回支付方交易关闭后的结果
375448 */
376449 @ Override
377- public Map <String , Object > close (AssistOrder assistOrder ){
450+ public Map <String , Object > close (AssistOrder assistOrder ) {
378451 throw new UnsupportedOperationException ("不支持该操作" );
379452 }
453+
380454 /**
381455 * 注意:最好在付款成功之后回调时进行调用
382456 * 确认订单并返回确认后订单信息
@@ -518,13 +592,11 @@ public Map<String, Object> refundquery(RefundOrder refundOrder) {
518592 JSONObject resp = getHttpRequestTemplate ().getForObject (getReqUrl (PayPalTransactionType .REFUND_GET ), authHeader (), JSONObject .class , refundOrder .getRefundNo ());
519593 return resp ;
520594 }
595+
521596 @ Override
522597 public Map <String , Object > downloadBill (Date billDate , BillType billType ) {
523598 return Collections .emptyMap ();
524599 }
525600
526601
527-
528-
529-
530602}
0 commit comments