Introduction
The Paze® API offers Merchants an easy, simple, and convenient way for their consumers to provide them with checkout information. A customers Paze wallet contains their credit and debit card information from participating Issuers as well as address information that can be used for shipping.
This document provides an introduction to integrating Paze checkout within a native mobile application.
Prerequisites
To use the Paze API, Merchants must establish a relationship with an approved Paze Distributor who will sponsor and onboard the Merchant. Upon onboarding, Merchants will be assigned entity identifiers (such as API keys and profile IDs) and exchange required cryptographic material.
Onboard Instructions
This section requires that you have a merchant account. Reach out to our merchant integrations team to learn more about building with Paze via [email protected]. Download and populate the merchant setup form in preparation for your kickoff call with our implementation team.
- Paze API uses JWKS: JSON Web Key Set for secure authentication and communication.
- Generate the JSON Web Key Set then provide Paze with your public JWK. Paze will then generate an oAuth ID which will be used to create client assertion - then submitted as a token for 30 minutes of access to the Paze endpoints.
- Submit your public JSON web key via email to [email protected].
- In addition you will need a Paze test wallet that allows developers to run integration and API tests without the need to have an actual credit card in their Paze wallet. To complete the test wallet set up, be sure to provide a full name, email address, and a US mobile number to the Paze implementation team.
Common Definitions
Field Status
The field status column of the following tables uses letters to signify required, optional, or conditional characteristics of the parameter:
- R - Required: The request requires a value for this parameter.
- O - Optional: The request does not require a value for this parameter, but inclusion of the parameter in the request may change script behavior. See the Definition / Description column formore information.
- C - Conditional: The request requires a value for this parameter in specific circumstances. See the Definition / Description column for more information.
API Errors
API errors are reported using a standard error response structure. Logically, all errors are categorized into standard errors and business errors. Standard errors can be returned by any API and should be handled in a common way. Business errors are returned by only some APIs and are described in the errors section for each API.
Standard Error Response Structure
Unless noted elsewhere, the standard response structure (shown below) will be returned in case of an error. Use the reason field to drive your error handling logic. Errors are also provided with a human- readable description in the message field. This field should be used only to understand the problem. In rare cases, depending on the type of error, this error structure might not be returned and you should rely on the HTTP status code semantics.
| Field | Type | Max Size | Description | Field Status |
|---|---|---|---|---|
clientContext | String | 64 | Merchant-defined transaction identifier, reflected in response. | O |
ewSID | String | 36 | Globally unique identifier generated by the Service Operator. Returned by Merchant staff submitting support requests to the Service Operator.Character set: a-f, 0-9, - | R |
timestampISO8601 | String | 24 | Timestamp of message in ISO 8601 extended combined format. Format: Time stamp when response is sent by the Service Operator. | R |
error | Object | Container for the error details. | R | |
error.reason | String | 64 | Reason for the error. Use this field to drive our error handling business logic. | R |
error.message | String | 1024 | Free form text message describing the error condition. Do not show this message to the end user. Do not use this field to drive error handling logic. Use the reason field instead. The server can change this message at any point in time. | R |
error.details | Array | Array of fields, which failed validation. Populated if there are field level error details. | ||
error.details[].reason | String | 128 | Error reason | C |
error.details[].source | String | 128 | The value of this field is using XPATH expression to point to the field that failed validation. | C |
error.details[].message | String | 256 | The specific error for this field. |
//Example:
400 Bad Request
Server: pazeserver
Date: Mon, 06 Jul 2020 13: 44: 07 GMT
Timestamp: 1594043046
Content - Type: application/json
Content - Length: 1633
{
"clientContext": "clientContextString",
"ewSID": "string",
"timestampISO08601": "2021-08-23T21:59:57Z",
"error": {
"reason": "INVALID\_ARGUMENT",
"message": "Missing or invalid value specified in request.",
"details": [
{
"reason": "INVALID\_VALUE",
"source": "expirationMonth",
"message": "Should be a numeric value"
}
]
}
}Standard Errors
Standard errors can be returned by any API and should be handled consistently in a unified way. These errors may not be documented as part of each individual API description.
| HTTP StatusCode | Reason | Description |
|---|---|---|
400 | INVALID_ARGUMENT | The value provided for one or more request parameters is considered invalid. For example:
|
400 | INVALID_REQUEST | The API server could not understand the request.This sometimes occurs when a data field is not in the appropriate/particular format. For example:
|
401 | AUTH_ERROR | Unauthorized, see error object for details, e.g., API credentials do not match, or authorization token validation failure |
403 | FORBIDDEN | Forbidden, see error object for details, e.g., invalid client identity (origin) |
422 | As provided by the TSP | The error occurred at the TSP (Token Service Provider) while processing the provisioning request. The error structure will provide the details as provided by the TSP. |
429 | TOO_MANY_REQUESTS | The requestor is exceeding the concurrent request limit. |
500 | SERVICE_ERROR | Internal server error, see error object for details |
Mobile Client UX Integration
This section describes how to launch the UI/UX from a mobile application and return to the mobile application.
Once the mobile app receives thePazecheckout URL from the /checkout/session/create API call, and the consumer taps the Paze button (or related action), the mobile app needs to open the Paze checkout URL in the specific WebView control for the given mobile operating system. The WebViews will maintain state between multiple invocations and across the main browser as well asother apps, providing the best experience to a returning consumer.
| Operating System | WebView Control to Use |
|---|---|
| iOS | ASWebAuthenticationSession |
| Andriod | Custom Chrome Tab |
When the consumer exits the Paze UX, the Paze API redirect the WebView to the callbackURLScheme that the Merchant has provided in the /checkout/session/create API call.
It is expected that this URL is provided in a native mobile format that will cause control to return to the native mobile app.
On redirect to this schema, the Paze API will append query string parameters of status and a hash(#) param response.
- "status" parameter with one of the following values:
- success - the consumer successfully completed the Paze checkout steps, and the Merchant can utilize the other Paze APIs to get data to display or to get the secure payload
- canceled – the consumer cancelled out of the Paze UX before the last step
- failed – there was some error encountered during the Paze UX flow
- success - the consumer successfully completed the Paze checkout steps, and the Merchant can utilize the other Paze APIs to get data to display or to get the secure payload
- “response” parameter, includes a Base64URL encoded string with a JWS containing the minimal 'maskedCard' structure in the claim section. Once decoded the JWKS is the code for review/complete call.
The minimal “maskedCard” structure:
| Field | Type | Max Size | Definition / Description | Field Status |
|---|---|---|---|---|
maskedCard.panLastFour | String | 4 | Last 4 digits of the card. | R |
maskedCard.paymentCardDescr iptor | String | 32 | A free-form string (max of 32 characters) used for card/program recognition. Example ‘Travel Rewards', ‘Cashback Rewards' etc. | R |
maskedCard.paymentCardNetwork | Enum | Valid values:
| R | |
maskedCard.paymentCardType | Enum | Valid values:
| R | |
maskedCard.paymentCardBrand | Enum | Brand of payment instrument. Valid values:
| R | |
maskedCard.digitalCardData | Object | Digital Card Art. | R | |
maskedCard.digitalCardData. artUri | String | 1024 | URI that hosts the Card Art image to be used for presentation purposes. | R |
maskedCard.digitalCardData.artHeight | Integer | Height of art card image, in pixels. | R | |
maskedCard.digitalCardData.artWidth | Integer | Width of art card image, in pixels. | R | |
maskedCard.changeCardUrl | String | 10K | URL to change card | R |
“response” parameter example:
{
"maskedCard": {
"panLastFour": "<String>",
"paymentCardDescriptor": "<String>",
"paymentCardNetwork": "<String>",
"paymentCardType": "<String>",
"paymentCardBrand": "<String>",
"digitalCardData": {
"artUri": "<URL>",
"artHeight": < Integer > ,
"artWidth": < Integer >
},
"changeCardUrl": "<URL>"
},
"authorization": {
"intentId": "<String>"
}
}
For example, if the provided callbackURLScheme was “pazeCheckoutResponse”, then on redirect after successful completion of thePazecheckout flow, the redirect URL will look like this:
“pazeCheckoutResponse://?status=success#response = Rm9ybWF0OmJhc2U2NFVSTEVuY29kZShKVM6aGVhZGVyLmNsYWltLnNpZ25hdHVyZQ==”Implementation Examples
iOS
This section will describe the iOS specific setup and code to launch the Paze checkout experience in a webview. The webview that must be used for iOS is the ASWebAuthenticationSession.
- Import the
AuthenticationServicesframework in the project file.- import AuthenticationServices
- Configure the URL Scheme
- Open your Xcode project.
- Go to the project settings, select your target, and open the "Info" tab.
- Add a new URL Type by clicking on the plus button.
- Set the callback URL scheme (e.g., com.domain.appid).
- ASWebAuthenticationSession takes two parameters:
- url
- callbackURLScheme
Implementation
class PazeViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
// startPaze should be called when user hit PAZE checkout button
func startPaze() {
/* pazeURL will be returned in /checkout/session/create response.this is the same, callbackURLScheme which is passed in createCheckoutSession, generally it is app’s bundle identifier( like – com.domain.appName ) */
let callbackPazeScheme = "com.domain.appid"
let sessionPaze = ASWebAuthenticationSession.init(url: pazeURL, callbackURLScheme: callbackPazeScheme) {
callbackURL,
error in guard
let successCallbackURL = callbackURL,
error == nil
else {
// Handle error; checkout with Paze has not be successful.
return
}
let queryItems = URLComponents(string: successCallbackURL.absoluteString) ? .queryItems
// Extract 'status' and 'response' from queryItems and proceed accordingly
}
session.presentationContextProvider = self
session.start()
}
func presentationAnchor(
for session: ASWebAuthenticationSession) - > ASPresentationAnchor {
return self.view.window!
}
}Android
This section will describe the Android specific setup and code to launch thePazeexperience in a webview. The webview that must be used for Android is the Custom Chrome Tab (CCT).
- Add Dependencies in
build.gradlfile.
implementationandroidx.browser:browser:<version> - Configure the Custom URL Scheme
- Open your AndroidManifest.xml.
- Add an intent filter to handle the callback URL scheme (e.g., 'com.domain.app_name').
AndroidManifest.xml
<activity android:name=.PazeCallbackActivity”>
<intent-filter>
<action android:name=”android.intent.action.VIEW” />
<category android:name=”android.intent.category.DEFAULT” />
<category android:name=”android.intent.category.BROWSABLE” />
<data android:scheme="<com.domain.app_name>"; android:host=”callback” />
</intent-filter>
</activity>
Implementation
Launch the WebView
import android.content.Intent;
import android.net.Uri;
import androidx.appcompat.app.AppCompatActivity;
import androidx.browser.customtabs.CustomTabsIntent;
public class PazeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_paze);
startPaze();
}
private void startPaze() {
// pazeURL will be returned in createCheckoutSession response
Uri uri = Uri.parse(pazeUrl);
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.intent.setData(uri);
customTabsIntent.launchUrl(this, uri);
}
}
Handle the Callback
public class PazeCallbackActivity extends AppCompatActivity {
/* This is the same callbackURLScheme which is passed in
createCheckoutSession; generally it is app’s applicationId (like –
com.domain.appName )*/
private static final String CALLBACK_SCHEME_PAZE = "com.domain.app_name";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
if (uri != null && uri.getScheme().equals(CALLBACK_SCHEME_PAZE)) {
// Process 'status' and 'response' parameters and proceed accordingly
}
finish();
}
}API Backward Compatibility Considerations
The following changes are considered backward-compatible:
- Adding a new API resource or method.
- Adding a new optional request parameter to an existing API method.
- Adding a new value to a pre-defined list of values. The value can be added either in the request or the response
- Relaxing some of the constraints on an existing request parameter. For example, making it optional.
- Adding a new response parameter to the API response.
- Changing the format of opaque string identifiers.
In simple terms, these rules mean:
- Ignore any unknown fields received as part of API responses.
- Code to deal with unknown values received as part of API responses in a graceful manner.
- Do not make any assumptions except length about opaque string identifiers.
- The Service Operator may add new optional parameters to existing APIs and relax constraints on existing request parameters at any time.
We consider “additive” changes or changes to field values already flagged as having the potential to change, non-breaking.
“Reductive” changes; removing fields, end points, payload entries or changes to fields that were not noted as having a potential to change are all considered breaking changes.