Paypal enables cross-border payments
Springboot Actual e-commerce project Mall4j (https://gitee.com/gz-yami/mall4j)
Abstract: Paypal payment connection (V2)
1. Merchants register Paypal accounts
1. Preparation before registration
2. Verify after registration
Log in to the Developer Center
Use the account registered in the previous step to directly log in to the Developer Center.developer.paypal.com
1. Click the button “Dashboard” in the upper right corner
Click finish to display
2. Create two test accounts in the sandbox
1. In the left navigation bar, click Accounts under the SandboxCopy the code
2. After entering the Acccouts interface, you can see that the system has two generated test accounts, but we do not use the test account given by the system, it is very difficult to create twoCopy the code
3. Click "Create Account" in the upper right corner to Create a Personal Account for the test userCopy the code
Create a China merchant accountCopy the code
Businesses and individuals, create, directly click create to automatically create complete, the default business/individual account has 5000 dollars and then create two accounts, respectively are north American businesses, personal accountCopy the code
4. Login to create a good account of the sandbox The sandbox login address: https://www.sandbox.paypal.comCopy the code
3. Create an application in the sandbox
Developer.paypal.com on My Apps & Credentials —-> Sandbox ——> Create App
App Name: indicates the Name of the application
App Type:
// Merchant – Accept payment as merchant (seller). Merchant – Accept payments as a Merchant (seller)
// Platforms — Transfer funds to sellers in the form of platforms (marketplaces, crowdfunding or e-commerce platforms)
Platform — Move payments to sellers as a Platform (Marketplace, crowdfunding, or e-Commerce Platform)
Sandbox Business Account: Select a Sandbox merchant Account to create
4. Access to clientId and clientSecret
Click on the create
Client ID is clientId and Secret is clientSecret 5. Create WEBHOOKS at the bottom of the application details page
Then click Add Webhook
I’ll hit OK
Java payment connection
Payment method:
Paypal offers a variety of payment methods, such as standard payment and fast payment, of which standard payment is regarded as best practice.
The main feature of standard payment is that only the paypal button needs to be integrated. All payment processes are controlled by paypal and the access party does not need to care about payment details. When the user completes the payment, paypal will notify the access party through synchronous PDT or asynchronous IPN mechanism. This method is lightweight, has the least docking difficulty, and has less invasion to the borrower.
Fast payment is relatively complicated, and the payment process is controlled by the access party through invoking three interfaces. When the user jumps to the paypal payment page from the web page of the access party, the first interface is triggered to apply for payment token from paypal. The authorization interface will submit the payment token obtained in the previous step. This process is controlled by paypal and no actual payment is made. Then the access party invokes the second interface to obtain the user’s authorization information, including the payment amount and payment products. After the basic information is checked correctly, the third interface is called to make the actual payment and deduction. After the deduction is successful, paypal will also make synchronous PDT and asynchronous IPN notification. This method is very flexible and has strong control, but the coding complexity is high and the invasion is great. Considering the actual situation, we chose the standard payment method to access paypal payment.
Notification Method:
The IPN and PDT notification modes of paypal payment are asynchronous IPN notification, which may have delay but has high reliability. If the host of the access party is unreachable, a retry mechanism is provided to ensure that the IPN notification reaches the access party as far as possible. After receiving the IPN notification, the access party needs to confirm it. The confirmation method is to call the IPN confirmation interface by taking the received IPN notification as the request body intact. PDT notification is real-time, but the reliability is not high, because the notification is only once, there is no retry mechanism, once the host is unreachable, such a message will be lost. It is recommended that IPN and PDT notifications be mixed to ensure timeliness and reliability. We adopted IPN and PDT notification mechanisms.
The Demo site: demo.paypal.com/c2/demo/hom…
V1 version has expired, we use V2 docking payment
V1 document: developer.paypal.com/docs/api/pa… V2 document: developer.paypal.com/docs/api/pa…
Four, directly on the code
The system pays with the default dollar currency created
So without saying much let’s just go to the code
Maven parameter introduction
<! --paypal--> <dependency> <groupId>com.paypal.sdk</groupId> <artifactId>rest-api-sdk</artifactId> <version>1.42.</version>
</dependency>
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>checkout-sdk</artifactId>
<version>1.02.</version>
</dependency>
Copy the code
PayPalConfig.class
package com.xxxx.config;
import cn.hutool.core.util.StrUtil;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.OAuthTokenCredential;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.core.PayPalEnvironment;
import com.paypal.core.PayPalHttpClient;
import com.paypal.http.HttpResponse;
import com.paypal.orders.*;
import com.paypal.payments.CapturesGetRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.RefundRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.*;
/** * Obtain wechat payment information and login information through wechat configuration *@author cl
*/
@Slf4j
@Component
@AllArgsConstructor
public class PayPalConfig {
private static final String clientId = "xxxx";
private static final String clientSecret = "xxxx";
// Live the formal environment
private static final String mode = "sandbox";
private static final String PP_SUCCESS = "success";
/** * approved */
public static final String APPROVED = "approved";
/** ** /
public static final String PAYPAY_MODE = "live";
/** * PayPal cancels payment callback address */
public static final String PAYPAL_CANCEL_URL = "/result/pay/cancel";
/** * PayPal cancels payment callback address */
public static final String PAYPAL_SUCCESS_URL = "/result/pay/success";
/** * Current currency abbreviation, the default currency is EUR CNY USD */
public static final String CURRENTCY = "USD";
/** * approval_url validates url */
public static final String APPROVAL_URL = "approval_url";
public static final String CAPTURE = "CAPTURE";
public static final String BRANDNAME = "Supernote";
public static final String LANDINGPAGE = "NO_PREFERENCE";
public static final String USERACTION = "PAY_NOW";
public static final String SHIPPINGPREFERENCE = "SET_PROVIDED_ADDRESS";
public static final String COMPLETED = "COMPLETED";
public Map<String, String> paypalSdkConfig(a){
Map<String, String> sdkConfig = new HashMap<>(16);
sdkConfig.put("mode", mode);
return sdkConfig;
}
public OAuthTokenCredential authTokenCredential(a){
return new OAuthTokenCredential(clientId, clientSecret, paypalSdkConfig());
}
public PayPalHttpClient client(a) {
PayPal payPal = shopConfig.getPayPal();
String mode = payPal.getMode();
String clientId = payPal.getClientId();
String clientSecret = payPal.getClientSecret();
PayPalEnvironment environment = StrUtil.equals("live",mode) ?
new PayPalEnvironment.Live(clientId, clientSecret) :
new PayPalEnvironment.Sandbox(clientId, clientSecret);
return new PayPalHttpClient(environment);
}
/** * Generate paypal payment order */
public OrdersCreateRequest createPayPalOrder(PayInfoDto payInfo) {
OrdersCreateRequest request = new OrdersCreateRequest();
request.header("Content-Type"."application/json");
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent(Constant.CAPTURE);
com.paypal.orders.ApplicationContext payPalApplicationContext = new com.paypal.orders.ApplicationContext()
.brandName("Shopping")
.landingPage(NO_PREFERENCE)
.cancelUrl(PAYPAL_CANCEL_URL)
.returnUrl(PAYPAL_SUCCESS_URL)
.userAction("PAY_NOW"); orderRequest.applicationContext(payPalApplicationContext); List<PurchaseUnitRequest> purchaseUnitRequests =new ArrayList<>();
PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
.description("Shopping")
// Generated order transaction number generated by the system
.customId(payInfo.getPayNo())
.invoiceId(payInfo.getPayNo())
.amountWithBreakdown(new AmountWithBreakdown()
.currencyCode(Constant.CURRENTCY)
// value = itemTotal + shipping + handling + taxTotal + shippingDiscount
.value(payInfo.getPayAmount().toString())
);
purchaseUnitRequests.add(purchaseUnitRequest);
orderRequest.purchaseUnits(purchaseUnitRequests);
request.requestBody(orderRequest);
return request;
}
/** * create order */
public String getExcetuteHref (OrdersCreateRequest request) {
HttpResponse<Order> response = null;
try {
response = client().execute(request);
} catch (IOException e) {
log.error("Failed to call paypal order creation, cause: {}", e.getMessage());
throw new YamiShopBindException("Paypal payment request failed");
}
String href = "";
if (response.statusCode() == 201) {
for (LinkDescription link : response.result().links()) {
if(link.rel().equals("approve")) { href = link.href(); }}}return href;
}
/** * The user is authorized to make the payment successfully. ** * The user is authorized to make the payment successfully. ** * The user is authorized to make the payment successfully. Put the money into my PayPal account */
public PayInfoBo captureOrder(String token){
PayInfoBo payInfoBo = new PayInfoBo();
OrdersCaptureRequest request = new OrdersCaptureRequest(token);
request.requestBody(new OrderRequest());
HttpResponse<Order> response = null;
try {
response = client().execute(request);
} catch (IOException e1) {
log.error("Failed to call paypal to deduct money, cause of failure {}", e1.getMessage() );
}
payInfoBo.setBizOrderNo(response.result().id());
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
log.info("Capture id: {}", capture.id());
log.info("status: {}", capture.status());
log.info("invoice_id: {}", capture.invoiceId());
/ / paypal transaction number
payInfoBo.setBizPayNo(capture.id());
// Merchant order number, previously generated order number with user ID
payInfoBo.setPayNo(capture.invoiceId());
payInfoBo.setIsPaySuccess(false);
if("COMPLETED".equals(capture.status())) {
// The payment was COMPLETED and the status is =COMPLETED
payInfoBo.setIsPaySuccess(true);
payInfoBo.setSuccessString(PP_SUCCESS);
}
if("PENDING".equals(capture.status())) {
log.info("status_details: {}", capture.captureStatusDetails().reason());
String reason = "PENDING";
if(capture.captureStatusDetails() ! =null&& capture.captureStatusDetails().reason() ! =null) {
reason = capture.captureStatusDetails().reason();
}
// Payment succeeded with status =PENDING
log.info("Payment succeeded, status =PENDING: {}", reason);
payInfoBo.setIsPaySuccess(true); }}}return payInfoBo;
}
/** * Check the deduction information after payment */
public Boolean getCapture(String captureId) {
// Deduction enquiry
CapturesGetRequest restRequest = new CapturesGetRequest(captureId);
HttpResponse<com.paypal.payments.Capture> response = null;
try {
response = client().execute(restRequest);
} catch (IOException e) {
log.info("Failed to query payment deduction: {}",e.getMessage());
return false;
}
log.info("Capture ids: " + response.result().id());
return true;
}
Copy the code
In the Controller code
/** * payment interface */
@PostMapping("/pay")
@SneakyThrows
public String pay(HttpServletResponse httpResponse,@Valid @RequestBody PayParam payParamPayInfo payInfo) {
payInfo.setApiNoticeUrl(notifyUrl);
OrdersCreateRequest request = payPalConfig.createPayPalOrder(payInfo);
String href = payPalConfig.getExcetuteHref(request);
return href;
}
Copy the code
Notice after payment
PayNotice.class
Landing/V2: * * * * customer payment paypal return path parameter example * https://xxxx/p//result/pay/success? token=9DT13847SW4445928&PayerID=B2YKY8DYERF46 */
@RequestMapping("/result/pay/success")
public String successPayPal(@RequestParam("token") String token, @RequestParam("PayerID") String PayerID){
// Perform the deduction
PayInfoBo payInfoBo = payPalConfig.captureOrder(token);
// To query the deduction information, use the third party transaction number 29911292F3566070S
payPalConfig.getCapture(payInfoBo.getBizPayNo());
// Change the order to paid state after successful deduction, and execute successful payment business
return ResponseEntity.ok(payInfoBo.toString());
}
/** * Cancel the interface */
@RequestMapping("/result/pay/cancel")
public String successPayPal(@RequestParam("token") String token, @RequestParam("PayerID") String PayerID){
// Cancel the order
return ResponseEntity.ok("cancel");
}
Copy the code
Springboot Actual e-commerce project Mall4j (https://gitee.com/gz-yami/mall4j)