Our integration with PayPal Java SDK
Whenever a web application service comes to the point where а user should pay some money, it becomes obvious that it needs some integration with а banking system. When our application reached that point, we chose PayPal. Optimistically developers can follow the integration guide, but for me it was insufficient, therefore I decided to share my experience with you. .
CHECKOUT FLOW
There are several scenarios that are supported. You can check the documentation: https://developer.paypal.com. I used PayPal Express Check Out because it was best for this application scenario. I recommend you to follow the link and review the “checkout flow” diagram. Simplified it goes like this:
1. Create a Payment
A merchant application (it is your site) sends an API request to PayPal to create a new payment. In this call every detail related to the goods or services is specified – items names, amounts, fee, taxes, etc., whatever is applicable to your goods. Also you specify callback URLs for cancel and approve operations. Callback URLs are the endpoints on the merchant application that handle the approved/cancelled payments.
Note: Do not use query parameters in the callback URLs like: <base-url>?paramName=paramValue otherwise you will suppress the query parameters that are normally send by PayPal. Those parameters are needed in one of the next steps.
After that Paypal knows all the details about that payment. And it expects that someone will authenticate to pay the product/service. The application receives 3 links in the response: self, approval and execute. You will need one of them in the next step to approve the payment.
2. Approve the Payment
In this step merchant application redirects users to the approval link that was given by PayPal in the previous step. Here the users will authenticate himself to PayPal and will review the item list and say: “Yes I want it, go on”. Keep in mind that this is not an API call, this is an interactive user visit on the PayPal site, where he can authenticate, review the payment, choose the payment method, cancel or approve it. How does your application know what he decided? Well, in the 1st step you have provided approval and cancel URLs. The User will be redirected to those URLs according to his decision.
3. Handle Approval Callback
When PayPal is ready with the payment approval, it redirectс the user back to the merchant approval URL specified in step 1. This approval endpoint needs to handle some query parameters in the request set by PayPal. I use only “PayerID” and “paymentId” (be careful for that difference in these cases). The merchant application should associate those IDs so that it is able to execute the payment in the next step.
Note: Keep in mind that what is returned by this callback will be actually returned to the user not to PayPal, because this request is redirected by PayPal, but actually created by the user’s browser. So that also redirection URLs would work even if it points to your local server like “https://localhost/<some-URL>” .
Usually the merchant sites return some confirmation page displaying again user’s basket and total amount to pay with big red “Confirm” button. I spare users from all the “are you sure” and make the process lighter, so we directly proceed with the next step.
4. Execute The Payment
This step is done again by the merchant’s application sending an API request to PayPal using payer id and payment id. It specifies who is paying for what and how much. Having a successful response the payment will be considered as successful. Sometimes there are required manual approvals on PayPal site if there are different currencies of the payment and the merchant’s account.
HOW TO: Checkout scenario
Ok now after the theoretical part let’s try to do our own API calls using PayPal JAVA SDK.
Add dependency
First I added PayPal dependency to the maven project. As I had some conflicts with logging I had to also exclude log4j-slf4j-impl, so the final dependency goes like this:
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>rest-api-sdk</artifactId>
<version>LATEST</version>
<exclusions>
<exclusion>
<artifactId>log4j-slf4j-impl</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>
Configuration
To perform PayPal API calls it is needed to pass the PayPal account credentials (client ID and client secret) and the mode (“live” or “sandbox”). I handle this as a project configuration and pass to the APIContext constructor. When “sandbox” mode is used no real money transfer is performed. The API calls will use the PayPal system dedicated for the development needs. In the productive scenario just switch the configuration to operate on real PayPal system.
For a fast start I used the following test user credentials (sandbox):
clientId: AYSq3RDGsmBLJE-otTkBtM-jBRd1TCQwFf9RGfwddNXWz0uFU9ztymylOhRS
clientSecret: EGnHDxD_qRPdaLdZz8iCr8N7_MzF-YHPTkjs6NKYQvQSBngp4PTTVWkPZRbL
Then I created a context in similar way:
APIContext apiContext = new APIContext(clientID, clientSecret, mode);
An own test account
I also created my own test accounts. First I created a real one in https://www.paypal.com. After I log in I went to https://developer.paypal.com/developer/accounts/ to manage my test accounts (for more details you can see the topic “Creating Sandbox Test Account”). I created my own merchant’s test account with its client ID and client secret, so that I use them in the initialization of the context. I also created a test user account (a buyer) that I granted plenty of virtual money in the sandbox system. The test accounts can be logged-in at https://www.sandbox.paypal.com/
Checkout flow starts on merchant’s server side
There is also PayPal JavaScript library available for merchant application. It can help create PayPalstyle button which opens a popup window and redirects business logic to your server. I decided to use our own button and not use the popup. I pass the control of the page directly to server side where the checkout flow begins.
Create a payment
To create a payment you need first to set it’s details, similar to this.
// ###Details
// Let’s you specify details of a payment amount.
Details details = new Details();
details.setShipping(“1”);
details.setSubtotal(“5”);
details.setTax(“1”);
// ###Amount
// Let’s you specify a payment amount.
Amount amount = new Amount();
amount.setCurrency(“USD”);
// Total must be equal to sum of shipping, tax and subtotal.
amount.setTotal(“7”);
amount.setDetails(details);
// ###Transaction
// A transaction defines the contract of a
// payment – what is the payment for and who
// is fulfilling it. Transaction is created with
// a `Payee` and `Amount` types
Transaction transaction = new Transaction();
transaction.setAmount(amount);
transaction.setDescription(“This is the payment transaction description.”);
// ### Items
Item item = new Item();
item.setName(“Ground Coffee 40 oz”).setQuantity(“1”).setCurrency(“USD”).setPrice(“5”);
ItemList itemList = new ItemList();
List<Item> items = new ArrayList<Item>();
items.add(item);
itemList.setItems(items);
transaction.setItemList(itemList);
// The Payment creation API requires a list of
// Transaction; add the created `Transaction`
// to a List
List<Transaction> transactions = new ArrayList<Transaction>();
transactions.add(transaction);
// ###Payer
// A resource representing a Payer that funds a payment
// Payment Method
// as ‘paypal’
Payer payer = new Payer();
payer.setPaymentMethod(“paypal”);
// ###Payment
// A Payment Resource; create one using
// the above types and intent as ‘sale’
Payment payment = new Payment();
payment.setIntent(“sale”);
payment.setPayer(payer);
payment.setTransactions(transactions);
Than I create the redirection URL that will be latter used by PayPal to redirect the user back to the merchant site. The redirection URL should not have any query parameters like: <base-url>?paramName=paramValue otherwise PayPal does not add its query parameters that are needed in one of the next steps.
// ###Redirect URLs
RedirectUrls redirectUrls = new RedirectUrls();
redirectUrls.setCancelUrl(myCancelCallBackUrl);
redirectUrls.setReturnUrl(myApproveCallbackUrl);
payment.setRedirectUrls(redirectUrls);
Then I proceed with the first real API call to PayPal system using the merchant user credentials and desired mode (live or sandbox). I handle PayPalRESTException for the payment creation call in case that something goes wrong there.
// Create a payment by posting to the APIService using a valid AccessToken
// The return object contains the status;
APIContext apiContext = new APIContext(clientId, clientSecret, mode);
Payment createdPayment = payment.create(apiContext);
As a result to the payment creation the PayPal API returns an object of Payment that contains several links (self, approval and execute). I extract the approval link and store the payment ID.
// ### Take Payment Approval Url
Iterator<Links> links = createdPayment.getLinks().iterator();
while (links.hasNext()) {
Links link = links.next();
if (link.getRel().equalsIgnoreCase(“approval_url”)) {
paypalRedirectLink = link.getHref();
}
}
I store the created payment ID to associate it with the current transaction. This ID will be latter used to execute the payment through API call. In this way I don’t need the “execute URL” to be stored.
Redirect to PayPal
After I have created the payment successfully the users can be redirected to PayPal so that they will authenticate themselves. In the PayPal system the users can check the payment details and decide to approve or cancel the payment.
I implemented the redirection by simple 302 HTTP redirection to the “paypalRedirectLink” from previous step – like this:
HttpHeaders redirectHeaders = new HttpHeaders();
redirectHeaders.add(“Location”, paypalRedirectLink);
return new ResponseEntity<>(redirectHeaders, HttpStatus.FOUND);
This is returned as a response to the client’s side UI redirection to our application endpoint.
Normally UI controls of an AngularJS application just result in a backend call and eventual change of the application state. In AgularJS this means just changed label of the URL. To achieve full page redirection to PayPal URL I have chosen client side Javascript like this:
$window.location.href = ‘/api/paypal/checkout/?pay=payId’;
Handle Approval Callback
Approval callback is a call from PayPal system to our system based on users decisions to continue with the payment. Our system now receives two important parameters: payment ID and payer ID. They identify who is paying for what.
String payerId = params.get(“PayerID”);
String paymentId = params.get(“paymentId”);
Notice that difference in cases of the parameter names – ID vs Id. I believe it is a mistake on PayPal’s side which stays like this because of the compatibility.
Now the merchant site can bring again the confirmation dialog with all the information about this transaction. This is the last chance to cancel the transfer of the money. But this approach makes the user experience more complicated by this additional step. Users have already had a possibility to review the payment and approved it. Therefore in our application, the flow directly proceeds with the execution.
Execute the payment
Now we have the payer ID and the payment ID. This is sufficient information to execute the payment. That can be done by a second API call like this:
PaymentExecution paymentExecution = new PaymentExecution();
paymentExecution.setPayerId(payerId);
Payment payment = new Payment();
payment.setId(paymentId);
payment.execute(apiContext, paymentExecution);
The execute method can throw PayPalRESTException and again needs to be handled properly by the application. That’s it. In this point the transaction is executed and users are charged.
Test it
I check my test PayPal users, and their balance to see that the virtual money were transferred successfully. Unfortunately there are some delays of several minutes before the transaction appears in PayPal UI. I check the transactions on both accounts – payer and merchant.
It is possible also to test the negative scenarios by setting the PayPal account to return errors.
I hope this article helped you to understand and make your PayPal integration. Any constructive feedback is welcome. Did you meet some difficulties in your project? If you would like to enrich this content by in another aspect, please leave a comment.