https://img.shields.io/maven-central/v/io.github.payone-gmbh/pcp-client-android-sdk
Welcome to the PAYONE Commerce Platform Client Android SDK for the PAYONE Commerce Platform. This SDK provides everything a client needs to easily complete payments using Credit or Debit Card, PAYONE Buy Now Pay Later (BNPL).
- Supported Android API Version
- Features
- Installation
- Usage
- Demonstration Projects
- Contributing
- Releasing the library
- License
In order to use the SDK your minimum SDK Version needs to be at least API 34.
- Creditcard Tokenizer: Securely tokenize credit and debit card information.
- Fingerprint Tokenizer: Generate unique tokens for device fingerprinting.
dependencies {
implementation("io.github.payone-gmbh:pcp-client-android-sdk:1.2.0")
}
The Credit Card Tokenizer now uses the new PAYONE Hosted Tokenization SDK. It securely collects and processes credit or debit card information in a PCI DSS-compliant way, returning a token for use in your server-side payment process.
To integrate the Credit Card Tokenizer feature into your Android application, follow these steps:
Host your HTML page locally (for development) or on a server. The page must contain the payment IFrame and submit button:
<div id="payment-IFrame"></div> <button id="submit">Submit</button>
For a more sophisticated example, see creditcard-tokenizer-example.html.
import com.payone.pcp_client_android_sdk.cctokenizer.CreditcardTokenizerFragment
import com.payone.pcp_client_android_sdk.cctokenizer.CreditcardTokenizerConfig
import com.payone.pcp_client_android_sdk.cctokenizer.IframeConfig
import com.payone.pcp_client_android_sdk.cctokenizer.UIConfig
import com.payone.pcp_client_android_sdk.cctokenizer.SubmitButtonConfig
val uiConfig = UIConfig(
formBgColor = "#64bbb7",
fieldBgColor = "wheat",
fieldBorder = "1px solid #b33cd8",
fieldOutline = "#101010 solid 5px",
fieldLabelColor = "#d3d83c",
fieldPlaceholderColor = "blue",
fieldTextColor = "crimson",
fieldErrorCodeColor = "green"
)
val config = CreditcardTokenizerConfig(
iframe = IframeConfig(
iframeWrapperId = "payment-IFrame",
height = 400,
width = 400
),
uiConfig = uiConfig,
locale = "de_DE",
submitButton = SubmitButtonConfig(
selector = "#submit",
element = null
),
environment = "test", // Use "live" for production
tokenizationSuccessCallback = { statusCode, token, cardDetails ->
Log.d("CC Success", "Tokenized card successfully")
Log.d("CC Success", "Status: $statusCode")
Log.d("CC Success", "Token: $token")
Log.d("CC Success", "Card Details: $cardDetails")
},
tokenizationFailureCallback = { statusCode, errorResponse ->
Log.e("CC Failure", "Tokenization of card failed")
Log.e("CC Failure", "Status: $statusCode")
Log.e("CC Failure", "Error: ${errorResponse["error"]}")
}
)
You must fetch the JWT from your backend before initializing the SDK.
val jwtToken = fetchJwtTokenFromBackend() // Implement this in your backend
For local development, host your HTML file using a local server (e.g. http-server
or similar). Use your computer's IP address or emulator's special address:
- Emulator:
http://10.0.2.2:8080/creditcard-tokenizer-example.html
- Physical device:
http://<your-computer-ip>:8080/creditcard-tokenizer-example.html
If you want to use the example app and host the HTML file locally over HTTP, you need to allow cleartext (HTTP) traffic in your Android app. This is only recommended for development and testing.
- Create network_security_config.xml
- Place the following file in
app/src/main/res/xml/network_security_config.xml
:
- Place the following file in
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain> <!-- Emulator -->
<domain includeSubdomains="true">LOCAL_IP</domain> <!-- Your computer's local IP -->
</domain-config>
</network-security-config>
- Reference it in your AndroidManifest.xml
- Add the attribute:
android:networkSecurityConfig="@xml/network_security_config"
- Example:
<application
...
android:networkSecurityConfig="@xml/network_security_config"
...>
...
</application>
-
Host your HTML file locally
- Use a local server (e.g.
npx http-server
,python3 -m http.server
, etc.) - For emulator: access via
http://10.0.2.2:8080/creditcard-tokenizer-example.html
- For physical device: use your computer's local IP, e.g.
http://192.168.1.100:8080/creditcard-tokenizer-example.html
- Use a local server (e.g.
-
Important: Remove network_security_config.xml for production
- Only use this config for development. In production, always use HTTPS and remove the
network_security_config.xml
reference from your manifest.
- Only use this config for development. In production, always use HTTPS and remove the
val fragment = CreditcardTokenizerFragment.newInstance(
config,
jwtToken,
"https://<your-server-address>/creditcard-tokenizer-example.html" // Use your actual server address
)
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment)
.commit()
iframe
: Configure the container and size for the payment iframe.uiConfig
: Customize the look and feel of the form fields.locale
: Set the language/locale for the form.submitButton
: Provide a selector or element for the submit button.tokenizationSuccessCallback
: Handle the token and card details on success.tokenizationFailureCallback
: Handle errors on failure.environment
: Choose "test" or "live" for the SDK environment.
- The SDK uses a JWT from your backend for secure initialization.
- All card data is handled inside the iframe and never touches your application code.
If you previously used the classic PAYONE Hosted IFrames, update your integration to use the new Hosted Tokenization SDK as shown above. The old fields
, defaultStyle
, and related config are no longer used.
For more details, see the demo project folder.
To detect and prevent fraud at an early stage for the secured payment methods, the Fingerprint Tokenizer is an essential component for handling PAYONE Buy Now, Pay Later (BNPL) payment methods on the PAYONE Commerce Platform. During the checkout process, it securely collects three different IDs to generate a snippetToken in the format <partner_id>_<merchant_id>_<session_id>
. This token must be sent from your server via the API parameter paymentMethodSpecificInput.customerDevice.deviceToken
. Without this token, the server cannot perform the transaction. The tokenizer sends these IDs via a code snippet to Payla for later server-to-Payla authorization, ensuring the necessary device information is captured to facilitate secure and accurate payment processing.
To integrate the Fingerprint Tokenizer feature into your application, follow these steps:
Kotlin
import com.payone.pcp_client_android_sdk.fingerprinttokenizer.FingerprintTokenizer
Create an instance with the payla partner ID, partner merchant ID and the environment (test or production).
Example:
val fingerprintTokenizer = FingerprintTokenizer(
context = this,
paylaPartnerId = "YOUR_PARTNER_ID",
partnerMerchantId = "YOUR_MERCHANT_ID",
environment = PCPEnvironment.Test
)
In order to retrieve the snippet token you call the following method:
Example:
fingerprintTokenizer.getSnippetToken {
if (it.isSuccess) {
val token = it.getOrNull() ?: ""
Log.d("Token: ", success)
} else {
val exception = it.exceptionOrNull()
Log.d("Get Snippet token error: ", exception?.message ?: "")
}
}
This snippet token is automatically generated when the FingerprintTokenizer
instance is created and is also stored by Payla for payment verification. You need to send this snippet token to your server so that it can be included in the payment request. Add the token to the property paymentMethodSpecificInput.customerDevice.deviceToken
.
For further information see: https://docs.payone.com/pcp/commerce-platform-payment-methods/payone-bnpl/payone-secured-invoice
The PAYONE Commerce Platform Client Android SDK provides a demonstration project for Google Pay integration. The integration uses the official Google Pay Button libraries which are available for various frameworks:
- Compose (@google-pay/compose-pay-button)
Our demo implementation uses this library.
Add the following dependencies to your build.gradle.kts
:
dependencies {
implementation("com.google.android.gms:play-services-wallet:19.2.1")
implementation("com.google.pay.button:compose-pay-button:1.2.0")
// ...other dependencies
}
In your Activity, set up the Google Pay button and handle the payment as shown below:
import com.google.android.gms.wallet.AutoResolveHelper
import com.google.android.gms.wallet.PaymentsClient
import com.google.android.gms.wallet.Wallet
import com.google.android.gms.wallet.WalletConstants
import com.payone.pcpclientandroiddemo.gpay.GooglePayRequestJson
import androidx.compose.ui.platform.ComposeView
class MainActivity : AppCompatActivity() {
private lateinit var paymentsClient: PaymentsClient
private val LOAD_PAYMENT_DATA_REQUEST_CODE = 991
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Set up Google Pay PaymentsClient
paymentsClient = Wallet.getPaymentsClient(
this,
Wallet.WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_TEST)
.build()
)
val composeView = findViewById<ComposeView>(R.id.compose_google_pay)
composeView.setContent {
val startPayment = remember { mutableStateOf(false) }
if (startPayment.value) {
startPayment.value = false
launchGooglePay()
}
GooglePayButton(onClick = { startPayment.value = true })
}
}
private fun launchGooglePay() {
val paymentDataRequestJson = GooglePayRequestJson.getRequestJson(
merchantId = "your-merchant-id",
merchantName = "Your Merchant Name",
totalPrice = "100.00"
)
val request = com.google.android.gms.wallet.PaymentDataRequest.fromJson(paymentDataRequestJson)
AutoResolveHelper.resolveTask(
paymentsClient.loadPaymentData(request),
this,
LOAD_PAYMENT_DATA_REQUEST_CODE
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == LOAD_PAYMENT_DATA_REQUEST_CODE) {
handleGooglePayResult(resultCode, data)
}
}
private fun handleGooglePayResult(resultCode: Int, data: Intent?) {
when (resultCode) {
Activity.RESULT_OK -> {
val paymentData = data?.let { com.google.android.gms.wallet.PaymentData.getFromIntent(it) }
if (paymentData != null) {
// TODO: process paymentData
Log.d("GooglePay", "Payment processed: \\${paymentData.toJson()}")
} else {
Log.d("GooglePay", "Payment processed but paymentData is null")
}
}
Activity.RESULT_CANCELED -> {
Log.d("GooglePay", "Payment canceled by user")
}
AutoResolveHelper.RESULT_ERROR -> {
val status = AutoResolveHelper.getStatusFromIntent(data)
Log.e("GooglePay", "Payment error: \\${status?.statusMessage}")
}
}
}
}
And the Compose Google Pay button:
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.pay.button.ButtonTheme
import com.google.pay.button.PayButton
import com.payone.pcpclientandroiddemo.gpay.GooglePayRequestJson
@Composable
fun GooglePayButton(onClick: () -> Unit) {
val allowedPaymentMethods = GooglePayRequestJson.allowedPaymentMethods
Box(modifier = Modifier.fillMaxWidth()) {
PayButton(
onClick = onClick,
allowedPaymentMethods = allowedPaymentMethods,
theme = ButtonTheme.Dark
)
}
}
Note: Replace
your-merchant-id
andYour Merchant Name
with your actual merchant details. Make sure your layout contains aComposeView
with the IDcompose_google_pay
.
You can find a demonstration project for each language including all features in the corresponding directories:
- Android: Check out the PCPClientAndroidDemo folder.
Important
Be aware that you will need to provide your own properties, for example AID, MID, PortalKey at all places which are prefixed with "YOUR_".
See CONTRIBUTING.md
- Checkout develop branch.
- Do the required changes.
- Use the version script to update to new version, e.g:
# from the root folder sh version.sh 1.2.3
- Create a pull-request into main branch.
- After merging the develop branch create a Git tag with the version.
This project is licensed under the MIT License. For more details, see the LICENSE file.