Decoding Documentation — PayPal Integration in Unity

Dan Schatzeder
9 min readApr 9, 2021


No one likes documentation until they can understand documentation. It is therefore that most of us have a standing dislike with all the various kinds of documentation in the world. In our search for specific answers we often find that in its attempt to explain everything, documentation seems to explain nothing. In spite of this, it is this very process of interpreting documentation that causes its undertakers to vouch for its essentiality, however painful it may be.

A good demonstration of this is the basic integration of PayPal into Unity. While this is a specific instance, it showcases and reinforces the cornerstones of discovering the unknown:

  • Simplest implementation in the smallest intervals
  • Serialization and debugging — Information in excess
  • Trial & Error — Different inputs to analyze different results
  • Research — Utilize the internet!

While not the most convenient, our best learning experience is to take it from scratch. We can consult PayPal’s Integration API throughout this process, assuming we’ve created & activated a Developer account, a Sandbox account, and a demo app; most of which is covered in the Getting Started section.

into the abyss

Having created our app, our first step will be to “exchange Client ID and Secret for an Access Token” which will allow us to make further requests to the server for a limited time. We’re provided with a CURL script template for our input, and a sample response in JSON.

It may look like gibberish, but keep in mind CURL and JSON are two major coding languages. A bit of googling will yield some fast results.

CURL Documentation

Firstly, searching for CURL Documentation will elaborate upon our intended input.

  • -v “makes curl Verbose… useful for debugging and seeing what’s going on”.
  • -u is User Info for server authentication.
  • -h are Headers to include when sending HTTP to a server.
  • -d is the specified Data in a POST request (the type we’re using, as evidenced by our first line of curl input)
CURL to C# Converter

Using language converters and documentation, we can glean some useful information.

Converting our CURL input to C# tells us we’ll be making a Post Request to the provided TokenURL using the provided headers, data, and user information.

JSON to C# Converter

Once our request is made, we’ll await, print, and observe our response.

Converting our sample output to C# in advance tells us our response will be in the form of a Class, or JSON Object. Looking through the contents of this class, we’ll find the Access Token we were looking for.

Keeping with good discovery cornerstones, we’ll serialize, debug, and cache each response. Our C# input uses HTTP Requests, but using Unity.Networking allows for utilization of UnityWebRequest, which is more convenient here. Here’s what our first step looks like in Unity.

Tool-tips are another underrated and convenient form of documentation. Tabbing through UnityWebRequest.Post will yield us a more useful set of arguments than the standard (string, string), which returns an error if tried normally.

Most of our code eventuates similarly from the earlier C# translation, with a few noted differences. Our Data type here differs in that our most useful set of arguments for a UnityWebRequest of Post type uses a WWWForm instead of a String, as our C# conversion suggests. A combination of tool-tips and documentation helps to understand the translation. Secondly, after yielding for our request response, we’re printing our results in text, while making an optional differentiation between Error and Success, to make debugging easier.

We already have a sample response class from the documentation, so let’s declare one in Unity. Feel free to include all the provided variables, but remember we were only particularly told we need the “access_token” string.

A single variable class allows for easier communication with JSON, which keep in mind stands for JavaScript Object Notation. Searching “JavaScript Object” will quickly tell us that it is a “container for named value called properties or methods.” Sound familiar? A value inside of a container, like we’ve just made.
In the infancy of any feature you’re developing, serialize/print/debug everything.

Executing our method in Unity returns our designated success response. Following that, it looks intimidating, but hearkening back to our first sample response will tell us it’s exactly what we’re looking for. We’ve got our own token now. Copy our JSON container, or what’s inside the { }, and convert it back to C# to see for yourself. Store our _accessToken for later use, and start our next Coroutine: Payment Creation.

2. Create PayPal Payment

We’ve now got some bread and butter methods of interpreting the existing documentation. Most of our process going forward is being faithful to those:

  • Convert all sample inputs and outputs to C#
  • Measure these against the documentation and dissect what we’re after

In our Step 2 sample input, we see a familiar provided URL, WebRequest.Post and Header, and fixed input values of “intent = sale” and “payment_method = paypal”. These are easy, but what’s new?

  • Header that now asks for our Token, easy to imitate
  • CURL Data body (-d) that takes an entire JSON object as opposed to our simple string from earlier
  • The instructions say our JSON object must include all of the above and a Transaction array.

Let’s remember to take advantage of converting between C# and JSON inside of Unity and out. Take our sample input into C#. Observing the variables will indicate to us that this class has all the contents of a Cart or Order, which we’re all familiar with having shopped online at some point.

Turn that input into a C# Class, serialize everything, and create a reference to that class for inspector variable assignment. Include all the variables to start. You can fill in only the minimum or you can fill in all fields. Remember, trial and error is a great friend in introductory times. More on this later.

We’ll be converting our Order/Cart class to JSON before passing the Data through as a string in our WebRequest. Just like before, we’ll set headers, convert to bytes for our UploadHandler, and error check to differentiate between printed responses. The final steps of the “else” statement can be ignored for now. Working method below:

Tooling around with this will introduce us to the error debugging system, a few of which are on display below.
Error debugging is meticulous but valuable. In your basic implementation, you can choose to debug the proper syntax for each variable, or even experiment with deleting unnecessary variables entirely.

Starting with all blank variables and filling out the necessary components as I ran through a dozen or so errors, I wanted to showcase a few of them and what they mean. Mostly, I tried to delete each variable that PayPal or a Response error didn’t explicitly tell me was a required field. Below, we start with the full converted sample class, and end with our simplified Cart class.

Spoiler: JSON doesn’t support properties ({get; set;}). You’ll have to replace these manually, which can easily be done with the find & replace feature (CTRL+H in Visual Studio)

Passing through the necessary components will return us a successful response, which as always, we’ll convert into C#, cache, and observe. Going forth, one of our most important tasks will be differentiating between seemingly similar responses. So what’s different between our Cart and our Order Response? PayPal tells us to look out for a PayID, which we see, but there are also some URL’s included which we’ll find we need use for later. Keep that in mind and move on to Payment Approval.

3. Get Payment Approval

Our next step isn’t very descriptive, but what do we know we need next? We need some method of prompting payment approval from the customer. We don’t have a payment client, so let’s stick to what we know, WebRequests. Remember the only new variables in our C# Order Response were the PayID and some URLs. Experimenting with opening these will return to us two “authentication errors” and one page which prompts us to log into PayPal, revealing a fully prepared Payment Verification page in URL[1], which is labeled “approval_url” in the “rel” field. So we have a way to prompt the user for approval with this. Let’s open this link in our next Coroutine.

Next, our two mystery URLs. Observing the “rel” labels in each of these tells us that URL[2] is our Execute link. Step 4 is Executing payment, so we can compare those URLs and conclude that this is a link useful to us in the next step. By process of elimination, we can test out requesting responses from URL[0], which is called “self” and has a method called “GET”.

We’re asking for a response from URL[0], and remember we want to know specifically when the user has completed payment approval, so this time we’ll be making continuous web requests until our condition is satisfied. What’s our condition? We don’t know yet, but let’s pace out our requests a few seconds at a time so that we don’t get locked out of the server for overloading.

This step didn’t designate any data to be passed through, so just ping the URL with our normal headers, error check, and observe the response. Since this is the least documented step in the process via PayPal, we need to look closely at the responses we’re getting. Take a continuous response every 5 seconds. While this is happening, manually navigate through our generated URL[1] and simulate the approval of payment. Now observe our responses before and after payment.

Same same — but different.

At this point, we should be converting every response we get to C#, which would tell us that our before response and our after response are similar entirely different classes. But even in our console, observing the first line of our JSON object response, we notice that a significant difference is a status variable that is loudly set to “VERIFIED”; a pretty good approval indicator!

There’s how we tell we succeeded, and how we tell our Approval Routine to stop and move onto the next. Forecasting into Step 4, we can see we’ll need the payer_id to complete the next step. Store him, and move onto the last leg.

4. Execute Payment

Reading Step 4, there’s actually nothing new to us. A Post Request to our ExecuteURL, two familiar headers, and a data field with a single contained variable. Keep only in mind that our data asks not for a single variable like in Step 1, but for a JSON object that contains a single variable, which is indicated by the { ….. }. There’s already an example of a class that contains a single variable in this article, so all the tools to complete Step 4 organically are here. Tackle that, and you’ve got a working sandbox PayPal payment system!

It’s important to note that going live with PayPal is a task that should be handled differently.

Firstly, this should only be used in Standalone PC & WebGL environments. Using PayPal on mobile or other marketplace environments will likely get you banned as a developer.

Second, for security reasons, you definitely won’t want all of your data cached like we do here. Specifically the Client ID, Secret, and Access Token. Ideally, you’ll have those pulled from a server or at the very least encrypted — not stored plainly in code.

Lastly, you’d want a dynamic method of creating your cart, accurately adding together subtotals, tax, fees, and totals on your own before converting them to strings for your JSON input.

If you’ve made it this far, none of the above is out of reach, just a new and separate process of discovery that should utilize the same principles we have here. Research. Small, simple steps. Trial and error .Serialize, debug, print.

Learn anything with patient diligence ᴬᴺᴰ ᴬ ᶜᴼᴺˢᴵᴰᴱᴿᴬᴮᴸᴱ ᴬᴹᴼᵁᴺᵀ ᴼᶠ ᴾᴬᴵᴺ