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)