Client SDKs: Embedded Components
Embedded Components are similar to Hosted Payment Pages, with the difference being that the customer can supply the required information beforehand and the client SDK can control that flow natively without the need for redirecting to the PSP’s hosted page. This integration streamlines the user experience by keeping everything within the familiar environment of your app, creating a more cohesive transaction process.
Embedded Components alternative payment method flow
We have implemented the Native APM flow into our SDKs to make it easier for you to integrate Native alternative payment methods. If you wish to learn more about what happens in the background and the backend calls the ProcessOut SDK makes, please refer to the Server to Server: Embedded Components guide.
Below is a sequence diagram that shows the flow for a Native APM payment. The following sections explain the steps in more detail.
Setting up 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() // optional
...
import com.processout.sdk.api.ProcessOutApi
...
ProcessOutApi.configure(
ProcessOutApi.Configuration(
context = this,
projectId = "your_project_id"
)
)
...
List the available Native APMs providers
You can have several Native APMs configured for use with ProcessOut at the same time (go to Dashboard › Payment providers to see the available native APMs or configure new ones).
However, this does not mean that all of them will be available for a particular purchase. For example, some native APMs might not operate in every country. The ProcessOut API lets you find the set of native APMs that are available for an invoice so that you can list them for your customer to choose from.
The code sample below shows how to retrieve a list of available native APMs for your invoice.
client.fetchGatewayConfigurations({
invoiceID: "iv_tIWEiBcrXIFHzJeXcZzqyp8EpY0xwmuT",
filter: "native-alternative-payment-methods"
}, processoutAPMsReady, function(err) {
console.log("Woops, could not fetch APMs: "+err);
});
let request = POAllGatewayConfigurationsRequest(
filter: .nativeAlternativePaymentMethods
)
let response = await ProcessOut.shared.gatewayConfigurations.all(request: request)
// Coroutine function.
val request = POAllGatewayConfigurationsRequest(
filter = POAllGatewayConfigurationsRequest.Filter.NATIVE_ALTERNATIVE_PAYMENT_METHODS,
withDisabled = false
)
val result = ProcessOutApi.instance.gatewayConfigurations.fetch(request)
when (result) {
is ProcessOutResult.Success -> TODO()
is ProcessOutResult.Failure -> TODO()
}
// Callback function.
val request = POAllGatewayConfigurationsRequest(
filter = POAllGatewayConfigurationsRequest.Filter.NATIVE_ALTERNATIVE_PAYMENT_METHODS,
withDisabled = false
)
ProcessOutApi.instance.gatewayConfigurations.fetch(
request = request,
callback = object : ProcessOutCallback<POAllGatewayConfigurations> {
override fun onSuccess(result: POAllGatewayConfigurations) {
TODO()
}
override fun onFailure(e: Exception) {
TODO()
}
}
)
Build your UI components and launch the Embedded Components payment sheet
Once you have obtained your available native APM configurations you can construct your own UI components that will supply the gateway_configuration_id
and the invoice_id
. These can be passed to the client SDKs so that they can create and launch the native APM payment sheet for you.
<script>
// Use `setupNativeApm` function which accepts `gatewayConfigurationId`
// and `invoiceId` to create the Native APM instance.
var nativeApm = client.setupNativeApm({
gatewayConfigurationId,
invoiceId
});
...
</script>
...
<!-- Create a container div element on your page with a specific id
attribute. -->
<div id="napm-container"></div>
<!-- Mount the widget on the Web page using the `mount` function from the
Native APM instance. It accepts a selector of for the container `div`
element. -->
<script>
nativeApm.mount('#napm-container');
</script>
import ProcessOutUI
let configuration = PONativeAlternativePaymentConfiguration(
invoiceId: "invoice_id",
gatewayConfigurationId: "gateway_configuration_id"
)
let view = PONativeAlternativePaymentView(configuration: configuration) { result in
// TODO: Handle result
}
// Alternatively in UIKit environment you can use PONativeAlternativePaymentViewController.
// It is also created via init where the only difference is additional optional style
// argument
// 1) It is required to initialize the launcher in the onCreate()
// method of an Activity or Fragment.
private lateinit var launcher: PONativeAlternativePaymentMethodLauncher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launcher = PONativeAlternativePaymentMethodLauncher.create(from = this) { result ->
when (result) {
PONativeAlternativePaymentMethodResult.Success -> TODO()
is PONativeAlternativePaymentMethodResult.Failure -> TODO()
}
}
}
// 2) Launch the activity.
launcher.launch(
PONativeAlternativePaymentMethodConfiguration(
gatewayConfigurationId = "gway_conf_",
invoiceId = "iv_"
)
)
Once the customer is presented with the payment sheet they will be able to input the required information. The SDK handles the rendering of the UI components required to capture the customers' input and handles the communication with ProcessOut.
If any customer action is required (e.g. confirming the payment through their banking app), the client SDK will present a customizable page to the customer. It will then wait for the action to be completed before sending the response back to your app and handling the closing of the payment sheet.
Customization of the payment sheet
Mobile SDKs
We allow certain elements of the UI to be customizable on the payment sheet. You can customize the following:
- Title style and text
- Input elements style
- Buttons’ style and text/icons
- The background style
- Message styles (success/failure/information).
- Payment confirmation
The following code snippet gives you an example on how to do that for some elements. You can reference our mobile SDK docs for more information.
let payButtonStyle = POButtonStyle(
normal: .init(
title: .init(color: .black, typography: .init(font: .system(size: 14))),
border: .clear(),
shadow: .clear,
backgroundColor: .green
),
highlighted: .init(
title: .init(color: .black, typography: .init(font: .system(size: 14))),
border: .clear(),
shadow: .clear,
backgroundColor: .yellow
),
disabled: .init(
title: .init(color: .gray, typography: .init(font: .system(size: 14))),
border: .clear(),
shadow: .clear,
backgroundColor: .darkGray
),
progressStyle: .system(.medium)
)
let style = PONativeAlternativePaymentStyle(
actionsContainer: .init(primary: payButtonStyle, secondary: .secondary, axis: .horizontal)
)
// Apply style to view
PONativeAlternativePaymentView(...)
.nativeAlternativePaymentStyle(style)
// In UIKit pass style directly to controller's init
let viewController = PONativeAlternativePaymentViewController(
style: uiConfiguration, ...
)
// Rather than changing styling you could change displayed data, for example
// to change screen’s title and action button text you could do:
let uiConfiguration = PONativeAlternativePaymentConfiguration(
...,
title: "Pay here",
submitButton: .init(title: "Submit", icon: Image(systemName: "centsign")),
cancelButton: .init(disabledFor: 10, confirmation: .init())
)
PONativeAlternativePaymentView(configuration: uiConfiguration, ...)
val payButtonStyle = POButtonStyle(
normal = POButtonStateStyle(
text = POTextStyle(Color.WHITE, POTypography.actionDefaultMedium),
border = POBorderStyle(radiusDp = 16, widthDp = 0, color = Color.TRANSPARENT),
backgroundColor = Color.parseColor("#ffff8800")
),
disabled = POButtonStateStyle(
text = POTextStyle(Color.GRAY, POTypography.actionDefaultMedium),
border = POBorderStyle(radiusDp = 16, widthDp = 0, color = Color.TRANSPARENT),
backgroundColor = Color.LTGRAY
),
highlightedBackgroundColor = Color.parseColor("#ffffbb33"),
progressIndicatorColor = Color.WHITE
)
launcher.launch(
PONativeAlternativePaymentMethodConfiguration(
gatewayConfigurationId = uiModel.gatewayConfigurationId,
invoiceId = uiModel.invoiceId,
style = PONativeAlternativePaymentMethodConfiguration.Style(
submitButton = payButtonStyle
),
uiConfiguration = PONativeAlternativePaymentMethodConfiguration.UiConfiguration(
title = "Payment details",
submitButtonText = "Submit",
successMessage = "Payment confirmed.\nThank you!"
),
options = PONativeAlternativePaymentMethodConfiguration.Options(
isBottomSheetCancelable = true
)
)
)
Web SDK
Our widget comes with default styling, but you have the option to customize its appearance in two ways:
Inline styles
The Native APM instance offers a convenient setTheme
function, allowing you to style the widget using inline styles:
nativeApm.setTheme({
buttons: {
default: {
backgroundColor: 'green'
}
}
});
This function accepts a theme
object with the following schema:
{
wrapper: { // styles widget wrapper
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
alignItems: 'center',
width: '100%',
fontSize: '1rem',
},
spinner: { // styles loading spinner
width: '4rem',
height: '4rem',
border: '5px solid #f2f2f2',
borderBottomColor: '#7e57c2',
borderRadius: '50%',
display: 'inline-block',
boxSizing: 'border-box',
animation: 'rotation 1s linear infinite',
},
logo: { // styles merchant logo
width: '10rem',
height: 'auto',
marginBottom: '1rem',
},
buttons: {
default: { // styles buttons
borderWidth: '0px',
color: '#fff',
backgroundColor: '#7e57c2',
cursor: 'pointer',
padding: '0.8rem 1.5rem',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
marginTop: '0.8rem',
fontSize: '1rem',
borderRadius: '5px',
},
spinner: { // styles loading spinner in the buttons
position: 'absolute',
width: '1rem',
height: '1rem',
border: '3px solid #f2f2f2',
borderBottomColor: '#7e57c2',
borderRadius: '50%',
display: 'inline-block',
boxSizing: 'border-box',
animation: 'rotation 1s linear infinite',
},
},
form: {
inputs: {
text: { // styles text inputs
padding: '0 0.8rem',
height: '3rem',
border: '1px solid #d7d7d7',
borderRadius: '5px',
width: '100%',
fontSize: '1rem',
},
select: { // styles select inputs
height: '3rem',
width: '100%',
border: '1px solid #d7d7d7',
borderRadius: '5px',
fontSize: '1rem',
},
numeric: { // styles numeric inputs
display: 'flex',
flexDirection: 'row',
gap: '0.8rem',
character: { // styles single digit of numeric input
width: '3rem',
height: '3rem',
fontSize: '1.5rem',
border: '1px solid #d7d7d7',
borderRadius: '5px',
textAlign: 'center',
},
},
},
labels: { // styles input labels
display: 'block',
marginBottom: '10px',
requiredStar: { // styles red required start of the label
color: '#e74c3c',
marginLeft: '0.1rem',
},
},
errors: { // styles input error messages
display: 'block',
color: '#e74c3c',
fontSize: '0.9rem',
minHeight: '1rem',
marginTop: '0.3rem',
width: '100%',
},
},
message: { // styles widget messages
fontSize: '1.1rem',
},
actionImage: { // styles action images like pending view illustration, success icon
marginTop: '1.3rem',
width: '15rem',
height: 'auto',
},
};
While it is possible to override any of these properties, we strongly recommend refraining from modifying styles that govern the fundamental layout of the element.
CSS classes
You have the ability to customize the appearance of the elements on your page by overriding styles through CSS classes:
#napm-container .native-apm-button {
background-color: 'green',
}
Each element is associated with its unique CSS class. Here's the list of CSS classes available for customization:
CSS class | Element |
---|---|
.native-apm-widget-wrapper | Wrapper of the Native APM widget |
.native-apm-button | Buttons, for example the Submit button |
.native-apm-spinner | Loading spinner |
.native-apm-numeric-input | Numeric inputs |
.native-apm-numeric-input-character | Single digit of numeric inputs |
.native-apm-input-error | Input error messages |
.native-apm-input-label | Input labels |
.native-apm-payment-provider-logo | Payment provider logo |
.native-apm-message | Customer messages |
.native-apm-action-image | Action image, for example the success icon |
Data prefilling
To enhance the user experience while using the Native APM widget, you have the option to prefill input fields with user data. This can be achieved by utilizing the prefillData
function, which accepts an object containing user data:
nativeApm.prefillData({
email: '[email protected]',
phone_number: '+48123123123'
})
// 1. Define delegate
final class Delegate: PONativeAlternativePaymentDelegate {
func nativeAlternativePayment(
defaultValuesFor parameters: [PONativeAlternativePaymentMethodParameter]
) async -> [String: String] {
[:]
}
}
// 2. Pass a delegate when creating a view
let delegate = Delegate()
let view = PONativeAlternativePaymentView(delegate: delegate, ...)
// https://github.com/processout/processout-android/blob/master/sdk/documentation/NativeAlternativePaymentMethods.md#provide-default-values-for-payment-sheet-input-fields
viewModelScope.launch {
with(ProcessOut.instance.dispatchers.nativeAlternativePaymentMethod) {
// Subscribe for request to provide default values.
defaultValuesRequest.collect { request ->
// Default values should be provided as Map<String, String>
// where key is PONativeAlternativePaymentMethodParameter.key
// and value is a custom default value.
val defaultValues = mutableMapOf<String, String>()
// Populate default values map based on request parameters.
// It's not mandatory to provide defaults for all parameters.
request.parameters.find {
it.type() == ParameterType.PHONE
}?.also {
defaultValues[it.key] = "+111122223333"
}
// Provide response which must be constructed from request with default values payload.
// Note that once you've subscribed to 'defaultValuesRequest'
// it's required to send response back, otherwise the payment flow will not proceed.
// If there is no default values to provide it's still required
// to call this method with 'emptyMap()'.
provideDefaultValues(request.toResponse(defaultValues))
}
}
}
The supported data keys are the following:
Data key | Expected value |
---|---|
email | Valid email address |
phone_number | Phone number, including the international prefix and without spaces |
Events
The Native APM widget dispatches custom events at each step in the Native APM flow to facilitate seamless integration with your page.
Web SDK
You can easily monitor these events by utilizing the Event Listener Browser API:
window.addEventListener('processout_native_apm_payment_success', () => {
// Handle payment success.
})
Here is the list of events which the widget emits:
Event name | Description |
---|---|
processout_native_apm_loading | The widget is loading the gateway configuration |
processout_native_apm_ready | The widget is ready to accept customer input |
processout_native_apm_payment_init | The widget initiates the payment |
processout_native_apm_payment_additional_input | Additional customer input is required |
processout_native_apm_payment_success | The payment is successful |
processout_native_apm_payment_error | An error occurred during the payment |
processout_native_apm_gateway_configuration_error | An error occurred while retrieving the gateway configuration |
Mobile SDKs
On iOS you need to pass a delegate conforming to PONativeAlternativePaymentDelegate protocol in order to observe events.
During the module lifecycle SDK calls nativeAlternativePaymentMethodDidEmitEvent(_:) method and passes event object that you can inspect
Your app can subscribe to events using the following methods:
final class Delegate: PONativeAlternativePaymentDelegate {
func nativeAlternativePayment(didEmitEvent event: PONativeAlternativePaymentMethodEvent) {
// todo: handle event
}
}
let delegate = Delegate()
let view = PONativeAlternativePaymentView(delegate: delegate, ...)
viewModelScope.launch {
ProcessOut.instance.dispatchers.nativeAlternativePaymentMethod
.events.collect { event ->
when (event) {
PONativeAlternativePaymentMethodEvent.WillStart -> TODO()
PONativeAlternativePaymentMethodEvent.DidStart -> TODO()
PONativeAlternativePaymentMethodEvent.ParametersChanged -> TODO()
PONativeAlternativePaymentMethodEvent.WillSubmitParameters -> TODO()
is PONativeAlternativePaymentMethodEvent.DidSubmitParameters -> TODO()
is PONativeAlternativePaymentMethodEvent.DidFailToSubmitParameters -> TODO()
is PONativeAlternativePaymentMethodEvent.WillWaitForCaptureConfirmation -> TODO()
PONativeAlternativePaymentMethodEvent.DidCompletePayment -> TODO()
is PONativeAlternativePaymentMethodEvent.DidFail -> TODO()
}
}
}
Capturing the payment
The hosted page will attempt to automatically capture the payment and wait for a success or failure status for 30 seconds after the customer has entered the required information.
If you prefer to capture the payment manually or after 30 seconds, the status will remain unconfirmed. You can call the capture endpoint, with the gateway_configuration_id
as the source
parameter in the capture request.
More information can be seen here: Server to Server - Capture.
Updated about 1 month ago