Client SDKs: Hosted Payment Page
After reading the prerequisites, you will be able to generate invoice IDs that need to be sent to your frontend.
Those invoices will be used to list available payment providers.
The following code sample demonstrates how the ProcessOut API passes control to the payment method in a separate window and then regains control after the payment is complete. Note that the invoice ID used in the code is pre-generated on our server for demonstration purposes. The sections below explain how to create the invoice in your own code and accept payments with it.
Setting up the client SDKs
To set up the client SDKs you only need your project ID. Find examples of how to configure the SDKs below.
...
<!-- Load ProcessOut.js -->
<script src="https://js.processout.com/processout.js"></script>
<!-- Set up ProcessOut.js with a valid `projectId` -->
<script>
var client = new ProcessOut.ProcessOut(projectId);
...
import ProcessOut
import ProcessOutUI
let configuration = ProcessOutConfiguration.production(
projectId: "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x"
)
ProcessOut.configure(configuration: configuration)
PricessOutUI.configure()
...
import com.processout.sdk.api.ProcessOutApi
...
ProcessOutApi.configure(
ProcessOutApi.Configuration(
context = this,
projectId = "your_project_id"
)
)
...
<div class="container">
<form action="" method="POST" id="apms-form">
<div id="results">
<div id="errors"></div>
<div id="success"></div>
<div id="loading">Loading...</div>
<input id="success-token" class="hidden" />
</div>
</form>
</div>
/*! PocketGrid 1.1.0
* Copyright 2013 Arnaud Leray
* MIT License
*/
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
/* Clearfix */
.block-group {
*zoom: 1;
}
.block-group:before, .block-group:after {
display: table;
content: "";
line-height: 0;
}
.block-group:after {
clear: both;
}
.block-group {
/* ul/li compatibility */
list-style-type: none;
padding: 0;
margin: 0;
}
/* Nested grid */
.block-group > .block-group {
clear: none;
float: left;
margin: 0 !important;
}
/* Default block */
.block {
float: left;
width: 100%;
}
.b75 {
width: 74%;
}
.b25 {
width: 24%;
margin-left: 2%;
}
html, body {
background: #ECEFF1;
font-size: 16px;
}
.container {
width: 100%;
max-width: 400px;
background: white;
margin: 3em auto;
padding: 1em;
border-radius: 4px;
box-shadow: 0 5px 7px rgba(50, 50, 93, 0.04), 0 1px 3px rgba(0, 0, 0, 0.03);
}
input {
border: 1px solid #ECEFF1;
border-radius: 4px;
box-shadow: 0 5px 7px rgba(50, 50, 93, 0.04), 0 1px 3px rgba(0, 0, 0, 0.03);
padding: 0.5em;
width: 100%;
margin-bottom: 1em;
font-size: 14px;
min-height: 2em;
}
.button {
margin: 0; margin-bottom: 1em;
padding: 0.75em;
text-align: center;
box-shadow: 0 5px 7px rgba(50, 50, 93, 0.04), 0 1px 3px rgba(0, 0, 0, 0.03);
background: #3F51B5;
color: white;
border-radius: 4px;
border: 1px solid #303F9F;
cursor: pointer;
}
.hidden {
display: none;
}
#errors, #success, #loading {
margin-bottom: 1em;
text-align: center;
font-size: 0.9em;
color: #D84315;
}
#success {
color: #4CAF50;
}
#loading {
color: #bdc3c7;
}
document.addEventListener("DOMContentLoaded", function() {
var client = new ProcessOut.ProcessOut(
"test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x");
client.fetchGatewayConfigurations({
invoiceID: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
filter: "alternative-payment-methods"
}, processoutAPMsReady, function(err) {
document.getElementById("loading").className = "hidden";
document.getElementById("errors").innerHTML = "Woops, couldn't fetch APMs: "+err;
});
function processoutAPMsReady(confs) {
document.getElementById("loading").className = "hidden";
var formWrapper = document.getElementById("apms-form");
for (var conf of confs) {
var el = document.createElement("div");
el.className = "button";
el.innerHTML = "Pay with " + conf.gateway.display_name;
conf.hookForInvoice(el, function(token) {
document.getElementById("errors").innerHTML = "";
document.getElementById("success").innerHTML = "Your user went through the entire APM payment flow. Use the token below to verify the payment in your backend.";
document.getElementById("success-token").value = token;
document.getElementById("success-token").className = "";
}, function(err) {
document.getElementById("errors").innerHTML = err.message;
document.getElementById("success").innerHTML = "";
document.getElementById("success-token").className = "hidden";
});
// Inject our APM element in the form
formWrapper.appendChild(el);
}
}
});
List the available APMs and redirect
You can have several APMs configured for use with ProcessOut at the same time (go to Dashboard › Payment providers to see the available APMs or configure new ones). However, this does not mean that all of them will be available for a particular purchase. For example, some APMs might not operate in every country. The ProcessOut API lets you find the set of APMs that are available for an invoice so that you can list them for your customer to choose from.
Web
The code sample below shows how you can use ProcessOut.fetchGatewayConfigurations()
to retrieve a list of available APMs for your invoice and use them to generate payment links. The callback function (e.g. the parameter supplied to client.handleInvoiceActionAdditionalData()
) receives the token that the APM payment generates. You should send this back to your server for capture within the callback.
client.fetchGatewayConfigurations({
invoiceID: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
filter: "alternative-payment-methods"
}, processoutAPMsReady, function(err) {
console.log("Woops, could not fetch APMs: "+err);
});
var configFunctions = [];
function createHandler(i, conf) {
return function() {
client.handleInvoiceActionAdditionalData(
invoiceId,
conf,
{"issuer_country": "BE"},
function(token) {
// The customer completed the gateway tokenization flow.
// We can send the token back to our backend to finish the flow.
document.getElementById("success").innerHTML = "Success! Token "+token+" on customer "+document.getElementById('customer-id').value;
}, function(err) {
// An error occured during tokenization. This could just be the
// customer that canceled the tokenization, or an error with
// the payment gateway.
document.getElementById("errors").innerHTML = err.message;
});
};
};
function processoutAPMsReady(confs) {
var formWrapper = document.getElementById("apms-payment-form");
var elements = [];
for (var i = 0; i < confs.length; i++) {
var el = document.createElement("div");
el.className = "my-apm-link";
el.innerHTML = "Pay with " + confs[i].gateway.display_name;
configFunctions[i] = createHandler(i, confs[i]);
// Inject our APM element in the form.
formWrapper.appendChild(el);
elements[i] = el;
}
for (var i = 0; i < confs.length; i++) {
(function () {
var k = i;
elements[k].addEventListener("click", function() {
configFunctions[k]();
});
}());
}
}
iOS
To retrieve a list of available APMs use the ProcessOut.shared.gatewayConfigurations.all()
method:
// Retrieve all available alternative payment methods (APMs).
let gatewayConfigurations = try await ProcessOut.shared
.gatewayConfigurations
.all(request: .init(filter: .alternativePaymentMethods))
.gatewayConfigurations
Alternatively, you can fetch the list of available APMs directly from your backend by communicating with the ProcessOut API.
Once the user selects a gateway configuration, you can proceed with authorizing the invoice using the selected configuration:
Ensure your invoice has a valid
return_url
assigned that points back to your application. Doing so enables the SDK to pass control back to your application.
- If you rely on scheme-based deep links, refer to Apple’s article on Defining a custom URL scheme for your app. Alternatively, when creating the authorization request, you can set the
webAuthenticationCallback
property with a custom scheme that matchesreturn_url
.- For applications that support iOS 17.4, you can use universal links. In this case, the
return_url
host specified in the invoice must be associated with your app via associated domains (see Apple’s article on associated domains for more details). Additionally, when creating the authorization request, set thewebAuthenticationCallback
to an object that matches against HTTPS URLs.
// 1. Create an authorization request using the selected gateway
// configuration.
let authorizationRequest = POAlternativePaymentAuthorizationRequest(
invoiceId: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
gatewayConfigurationId: gatewayConfigurations.first!.id
)
// 2. Authorize the invoice using the ProcessOut SDK.
let response = try await ProcessOut.shared
.alternativePayments
.authorize(request: authorizationRequest)
// 3. Finish the charge on your backend with response.gatewayToken
// in case of success.
The SDK will perform a redirect during the authorization process and return a result containing the token generated by the APM. You should send this token back to your backend to complete the capture process.
By default, authorization sessions are private (ephemeral). While this ensures user privacy, it may affect the performance of some payment methods (e.g., PayPal).
If desired, you can improve the performance of certain APMs by opting for non-ephemeral sessions. To enable this, set
prefersEphemeralSession: false
when creating the request.let authorizationRequest = POAlternativePaymentAuthorizationRequest( invoiceId: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT", gatewayConfigurationId: gatewayConfigurations.first!.id, prefersEphemeralSession: false )
Effects of Non-Ephemeral Sessions:
- Improved Performance: Non-ephemeral sessions allow the browser to share cookies and other browsing data with the authorization session, reducing friction for the user.
- System Prompt: Users will see a system alert to confirm the session at the beginning of the authorization process.
Android
The code sample below shows how you can use ProcessOut.instance.gatewayConfigurations.fetch()
in the Android client SDK). The callback function receives the token that the APM payment generates. You should send this back to your server for capture within the callback.
// 1. Initialize a Custom Chrome Tabs launcher in the onCreate() method of
// your Activity or Fragment to register a callback.
private lateinit var customTabLauncher: POAlternativePaymentMethodCustomTabLauncher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
customTabLauncher = POAlternativePaymentMethodCustomTabLauncher.create(from = this) { result ->
when (result) {
is ProcessOutResult.Success -> TODO() // 5. Finish the charge on your backend with
// result.value.gatewayToken.
is ProcessOutResult.Failure -> TODO()
}
}
}
// 2. List all the available APMs. You could do as shown below or
// you can communicate with the ProcessOut API directly from your
// backend.
val gatewayConfigurationsRequest = POAllGatewayConfigurationsRequest(
filter = Filter.ALTERNATIVE_PAYMENT_METHODS
)
ProcessOut.instance.gatewayConfigurations.fetch(gatewayConfigurationsRequest).let { result ->
when (result) {
is ProcessOutResult.Success -> result.value.gatewayConfigurations
is ProcessOutResult.Failure -> result.code
}
}
// 3. Once the user chooses the needed configuration, create a POAlternativePaymentMethodRequest.
val alternativePaymentRequest = POAlternativePaymentMethodRequest(
invoiceId = "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
gatewayConfigurationId = gatewayConfigurations.first().id
)
// 4. Launch the activity.
customTabLauncher.launch(
request = alternativePaymentRequest,
returnUrl = "your.application.id://processout/return"
)
Pay using customer token
You can create a customer_token for APM payments to reuse the customer details for multiple payments. To learn more on APM payments tokenization and tokenized APM payments see Tokenizing Alternative Payment methods.
Some payment gateways require customer to confirm their payment during Customer Initiated Transactions (CITs) even after having tokenized the customer details. To use a customer_token for such payment, you may use the ProcessOut.fetchGatewayConfigurations()
flow (as described in the List the available APMs and redirect section). The only difference is that you must provide an optional customerTokenID
parameter:
client.fetchGatewayConfigurations({
invoiceID: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
// optional parameter to pay using the previously created customer_token
customerTokenID: "tok_Q2GtPwkWh8IjDNlzPKtm6LTm4ZfB5wUh",
filter: "alternative-payment-methods"
}, processoutAPMsReady, function(err) {
console.log("Woops, could not fetch APMs: "+err);
});
Handle the capture on the server
The callback for the APM response described above should send the new token back to the server along with the invoice ID. After this point, the process that you use to capture the payment on the server is exactly the same as the process for a card payment. See the page about capturing a payment for a full description.
Updated 26 days ago