Introduction

Last weekend a friend wanted to enable Paypal payments on his jee website. He didnt have a lot of time himself so he asked me to figure out how to do that.

For some time, I have been thinking about starting a blog about integrating different java technologies and third-party apis with each other, and I figured sharing my experience with integrating Paypal into a website might make for a nice first entry.

So, who are you?

This article is aimed at Java Web developers who have a need to integrate Paypal in their current project. The reader should know the Java programming language quite well, and have at least a basic understanding of servlets and Java web applications. Knowledge about web services and Axis in particular is not required, but definitely recommended.

What we are going to do

We will add a “Buy now”-button to a website for a particular good or service. This button will take us to the paypal site, where a customized checkout page will be shown. The user will be able to pay directly from that page, after which he will be returned to our website, where the order will be further processed, for example, by enabling the service on the spot or by adding the order for the good to a processing queue.

Note that we will be working on a particular use case made possible by the api. It is also possible to let the user only authorize the payment on the paypal site(instead of paying directly), after which he will complete the order and actually press the “pay now”-button on our own website. This alternate use case is not covered by this article.

Available material

The documentation on the subject can be found at Paypal API Documentation.
This tutorial is almost solely based on the Express Checkout Integration Guide, which is in my opinion the most relevant document for most paypal integrations.

We will also need to download some files at Paypal API SDK and Downloads.

The decision that needs to be taken here first is whether to choose for the name-value pair api or the soap web service api. Since I am quite familiar with soap and web services, I decided to go with this option.

So, if you want to go through with this tutorial, you need to download the PayPal API: SOAP Interface for Java on the page mentioned above.

Optional: Generating the soap client yourself.

Note that this skip can be skipped, since the downloaded soap interface is up to date with the wsdl.

The two most important files in the downloaded soap interface above are paypal-base.jar and paypal-stubs.jar. The first contains the compiled sdk while the latter contains the compiled soap client.

All the necessary wsdls and xsd schemas can be found at:

  • https://www.paypal.com/wsdl/PayPalSvc.wsdl
  • https://www.paypal.com/wsdl/eBLBaseComponents.xsd
  • https://www.paypal.com/wsdl/CoreComponentTypes.xsd
  • https://www.paypal.com/wsdl/EnhancedDataTypes.xsd

To generate a client for these files, we will use the wsdl2java tool.

If you are using Eclipse IDE, an easy way to do this is to install the Jboss Tools Eclipse Plugin, copy the above files into your IDE, rightclick on the main wsdl file and then select Web Services > Generate Client.

A better way however, is to incorporate the client generation into your production build process. When using the build tool Maven for example, you can use the axistools maven plugin to directly generate the client from the specified urls(axistools:wsdl2java). By incorporating it into your build, you make sure that your client will always be up to date.
For example, setOrderTotal for the SetExpressCheckoutService is deprecated right now, in favor of adding an array of PaymentDetails on the object passed to the service, and setting the order totals on those different PaymentDetails. It is likely that this deprecated part of the api will be removed at some point, and if your code would still be using this, would cause the soap call to fail. If this happens – you should be notified by your unit tests of this – you would need to download the most current version again and incorporate it into your project, which will be more time consuming then than having the client generation in your build process.

Step by step tutorial

First of all, I wanted to keep this article free of any other api or framework not included in the standard JDK, so we will discuss all code from easy to run java classes, which unfortunately, you will have to download through Easy Share, because WordPress does not allow uploading zip files. Setting up the actual html pages with forms mapped to the actual actions would just clutter the text and should be trivial for you anyway.

Now, I stole the following diagram from the documentation on the Paypal site. It gives a clear overview on what we are going to do.

Paypal Express Checkout Flow

  1. The user sends a request to our server by pressing some “buy now with paypal” button on our site. This button is most likely part of a form on a html page, of which the action is mapped to an url like http://www.ourserver.com/buy_our_service_now.xhtml.
  2. Our server receives the request. The server then performs the action mapped to this url, which will first execute the SetExpressCheckoutDetails method, providing Paypal with all the necessary details to render an appropriate checkout page. This method will return a token, which the server will use right away as a request parameter when redirecting to the paypal site.
  3. The user – redirected to the paypal site – authenticates with the paypal site, is shown the appropriate checkout page and does or does not agree to pay for the offered goods or services. In the basic use case, we will assume that the user presses “Pay now” on the paypal site, agreeing to pay for the goods/services, after which the paypal site redirects to our server, probably to an url like http://www.ourserver.com/process_checkout.xhtml?token=tokenvalue. The token is automatically appended by Paypal.
  4. Our server receives the request. The server then performs the action mapped to this url, which will first execute the GetExpressCheckoutDetails method, to get all details from paypal again that were initially passed to paypal by our own server. We then invoke the DoExpressCheckout method, providing the necessary details we just got back, to accept the payment of the user and complete the express checkout. If this method returns “Completed”, we process the checkout further on our server.

Doing SetExpressCheckoutDetails

We assume that the user has clicked our “buy now with paypal” button and the server received his/her request. So, obviously, with regard to the flow we mentioned in the previous section, we do not discuss step 1 since setting it up is trivial and we jump straight to step 2. We execute the SetExpressCheckoutDetails method, providing Paypal with all the necessary details to render an appropriate checkout page, as in the following class.

public class SetExpressCheckoutService{

	private static Logger log = Logger.getLogger(SetExpressCheckoutService.class);

	public static void main(String[] args){
		SetExpressCheckoutService setExpressCheckoutService = new SetExpressCheckoutService();

		//the parameters for the service
		Long userId = 5l;
		String amount = "25";
		String returnURL = "http://localhost:8080/integratingstuff-paypal/return_after_payment.xhtml";
		String cancelURL = "http://localhost:8080/integratingstuff-paypal/cancel_payment.xhtml";
		PaymentActionCodeType paymentAction = PaymentActionCodeType.Sale;
		CurrencyCodeType currencyCode = CurrencyCodeType.EUR;

		try {
			//calling the service, setting up the checkoutpage
			String token = setExpressCheckoutService.setExpressCheckout(userId, amount, currencyCode, returnURL,cancelURL,paymentAction);
			log.info("Url to redirect to: https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=" + token);
		} catch (PayPalException e) {
			log.error(e);
		}
	}

	public String setExpressCheckout(Long userId, String paymentAmount,
			CurrencyCodeType currencyCodeType, String returnURL, String cancelURL,
				PaymentActionCodeType paymentAction) throws PayPalException{
		CallerServices caller = new CallerServices();

		//construct and set the profile, these are the credentials we establish as "the shop" with Paypal
		APIProfile profile = ProfileFactory.createSignatureAPIProfile();
		profile.setAPIUsername("sdk-three_api1.sdk.com");
		profile.setAPIPassword("QFZCWN5HZM8VBG7Q");
		profile.setSignature("AVGidzoSQiGWu.lGj3z15HLczXaaAcK6imHawrjefqgclVwBe8imgCHZ");
		profile.setEnvironment("sandbox");
		caller.setAPIProfile(profile);

		//construct the request
		SetExpressCheckoutRequestType pprequest = new SetExpressCheckoutRequestType();
		pprequest.setVersion("63.0");

		//construct the details for the request
		SetExpressCheckoutRequestDetailsType details = new SetExpressCheckoutRequestDetailsType();

		PaymentDetailsType paymentDetails = new PaymentDetailsType();
		paymentDetails.setOrderDescription("Integrating Stuff Test Order");
		paymentDetails.setInvoiceID("INVOICE-" + Math.random());
		BasicAmountType orderTotal = new BasicAmountType(paymentAmount);
		orderTotal.setCurrencyID(currencyCodeType);
		paymentDetails.setOrderTotal(orderTotal);
		paymentDetails.setPaymentAction(paymentAction);
		details.setPaymentDetails(new PaymentDetailsType[]{paymentDetails});

		details.setReturnURL(returnURL);
		details.setCancelURL(cancelURL);
		details.setCustom(userId.toString());

		//set the details on the request
		pprequest.setSetExpressCheckoutRequestDetails(details);

		//call the actual webservice, passing the constructed request
		SetExpressCheckoutResponseType ppresponse = (SetExpressCheckoutResponseType) caller.call("SetExpressCheckout", pprequest);

		//get the token from the response
		return ppresponse.getToken();
	}
}
Note: the most simple way to run this class is to make a project in your IDE and add the class code to a test source folder; go the map in which you extracted the contents of the PayPal API: SOAP Interface and put all the files in the lib folder on the classpath. Then all the necessary dependencies will be satisfied.

The code is pretty self-explanatory. In the main method, we pass all necessary parameters to setup the checkout page at Paypal, after which we use those to build the request and execute it. Paypal responds with a token, unique for this order.

Setting the profile

In the code, a profile is set. This profile determines whether we execute our request on the sandbox or production environment, and as what shop/seller we authenticate with the Paypal server, together with the credentials for that shop/seller. Obviously, if these credentials are incorrect or do not exist, the request will not succeed. In the example, we use the default test account Paypal provides. As we will see later, you will need to create a developer account on http://developer.paypal.com to be able to create “test buyer” accounts, and it is probably better to authenticate with the credentials of that developer account, since then you will be able to customize “the test shop” and consult its transaction memory.

Constructing the details

A few things can be said about the construction of the details object. We pass along 4 things. The paymentDetails, the returnUrl, the cancelUrl and the userId. The paymentDetails object contains the orderspecific information, and in the example, we pass along what is like a minimal set: the description of the order, the invoiceId which should be unique per seller account, the price of the order and the paymentActionType, which is Sale in our example.
The returnUrl is the url paypal will forward the user to after payment and the cancelUrl is the url paypal will forward to upon cancellation of the payment by the user.
We also pass the userId in the custom part of the details. Although this is not necessary, it might help us when Paypal redirects to our returnUrl. We might need to be able to link our buyer to the user on our site then. We could store this information ourself, like link the token returned by Paypal to a particular user in our database, but by putting all information at the Paypal server, our own server does not need to remember anything about the initiated order process so far.

Redirecting to paypal

When running the code in the previous section, a line like

Url to redirect to: https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=tokenvalue

should have appeared on your console. This is the url the server should redirect to after setting up the checkout page as discussed in the previous section.

Rendering the checkout page on the sandbox

Please copy-paste this url into your webbrowser.

You will get a message that you need to log onto the sandbox first. This is the time you will need to register a developer account at http://developer.paypal.com. Please do so, and once finished, log in with the newly created account.

Once logged in, you will see a tab “Test accounts”.
Make one test account, of account type “Buyer”, and remember its username and password. This test account will be the account with which you shall simulate the buyer on the sandbox.
Make another test account, this time of type “Seller”. Replace the credentials we used so far in the code pointing to the generic merchant with the credentials of this test merchant account. These credentials can be found under the “API Credentials” tab from within your developer account. Do not fail to replace this code. When trying to do the doExpressCheckout method with the generic merchant account, you cant make assumptions about its current configuration, which might lead to unexpected behavior I believe.

Now, copy-paste the url again in your webbrowser and login to the sandbox with the test buyer account.
If everything went well, you should see a page like:

Order as shown at the Paypal website

As you can see, the checkout page was rendered according to the order details provided to the SetExpressCheckout method.

The redirect url

Now, let us take a look a closer look at the redirect url:

https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=tokenvalue

Every passed parameter is essential. The cmd parameter tells Paypal it has to render the checkout page. The useraction parameter tells Paypal the payment should occur right now. Because of this, Paypal will also render a “Pay now” button to the user, and also show all details about the order. If this parameter is not passed, the default of useraction=continue is assumed, making paypal believe the order itself will be processed on the site of the seller, and because of this, paypal will not render the order details such as the description and the price whatsoever. The token parameter of course, tells Paypal what order this request is about.

Doing GetExpressCheckoutDetails and DoExpressCheckout

We will assume that our buyer pressed “Buy now” on the checkout page for his particular order. He was redirected to the returnUrl, but in this tutorial, since we do not have a webserver running, nothing happened.

Now, what we would want our webserver to do, is, when receiving the returnUrl request, process the checkout right away.

First, we use the token to fetch all the expressCheckoutDetails from Paypal.

public class GetExpressCheckoutDetailsService {

	public GetExpressCheckoutDetailsResponseDetailsType getExpressCheckoutDetails(String token) throws PayPalException{
		CallerServices caller = new CallerServices();

		APIProfile profile = ...;

		GetExpressCheckoutDetailsRequestType pprequest = new GetExpressCheckoutDetailsRequestType();
		pprequest.setVersion("63.0");
		pprequest.setToken(token);

		GetExpressCheckoutDetailsResponseType ppresponse = (GetExpressCheckoutDetailsResponseType)caller.call("GetExpressCheckoutDetails", pprequest);

		return ppresponse.getGetExpressCheckoutDetailsResponseDetails();
	}
}

Paypal returns all details about the expressCheckout, in pretty much the same format as we passed them to Paypal in the first place. Some fields, like payerInfo, will be filled in now though.

We then use the response of the GetExpressCheckoutDetails method to execute the DoExpressCheckout method, which will accept the payment of the buyer if possible.

public class DoExpressCheckoutService {

	private Logger log = Logger.getLogger(this.getClass());

	public boolean doExpressCheckoutService(GetExpressCheckoutDetailsResponseDetailsType response) throws PayPalException{
		CallerServices caller = new CallerServices();

		APIProfile profile = ...;

		DoExpressCheckoutPaymentRequestType pprequest = new DoExpressCheckoutPaymentRequestType();
		pprequest.setVersion("63.0");

		DoExpressCheckoutPaymentResponseType ppresponse= new DoExpressCheckoutPaymentResponseType();

		DoExpressCheckoutPaymentRequestDetailsType paymentDetailsRequestType = new DoExpressCheckoutPaymentRequestDetailsType();
		paymentDetailsRequestType.setToken(response.getToken());

		PayerInfoType payerInfo = response.getPayerInfo();
		paymentDetailsRequestType.setPayerID(payerInfo.getPayerID());

		PaymentDetailsType paymentDetails = response.getPaymentDetails(0);
		paymentDetailsRequestType.setPaymentAction(paymentDetails.getPaymentAction());

		paymentDetailsRequestType.setPaymentDetails(response.getPaymentDetails());
		pprequest.setDoExpressCheckoutPaymentRequestDetails(paymentDetailsRequestType);

		ppresponse= (DoExpressCheckoutPaymentResponseType) caller.call("DoExpressCheckoutPayment", pprequest);
		DoExpressCheckoutPaymentResponseDetailsType type = ppresponse.getDoExpressCheckoutPaymentResponseDetails();

		if (type != null){
			PaymentInfoType paymentInfo = type.getPaymentInfo(0);
			if (paymentInfo.getPaymentStatus().equals(PaymentStatusCodeType.fromString("Completed"))){
				log.info("Payment completed.");
				return true;
			}
			else {
				log.info("Payment not completed.. (" + paymentInfo.getPaymentStatus() + ")");
				return false;
			}
		}
		else {
			log.info("Problem executing DoExpressCheckoutPayment.. Maybe you tried to process an ExpressCheckout that has already been processed.");
			return false;
		}
	}
}

If the paymentstatus after doExpressCheckout returns “Completed”, you should take additional actions, like activating the requested service on your website or adding the order to some queue. If the paymentstatus is something else, pending for example, additional code should be written, for example, to poll Paypal again after 24 hours.

Note: note that doExpressCheckout can only be executed once.
Note also that using the by Paypal provided API credentials will cause the doExpressCheckout to end in “Pending” most of the time. Use the credentials of one of your test merchant accounts.