XPay Android Native SDK
This documentation provides instructions on adding the XPay Android Native SDK to an android project. It covers setup for both Kotlin and Java languages, event handling, UI customization, and payment processing.
Prerequisite Dependancy
implementation("com.squareup.okhttp3:okhttp:4.9.2")E2E Integration Sample Repo
Gradle Configuration Add JitPack in the dependency resolution management section:
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}For Older Gradle Versions:
allprojects {
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}Add the XPay SDK Dependency
// for production
dependencies {
implementation ("com.github.XStakCommerce:XPay-Element-Android-Native-SDK:2.1.5")
}
// for stagging
dependencies {
implementation ("com.github.XStakCommerce:XPay-Element-Android-Native-SDK:stage-2.1.5")
}Foreground Service Permissions For EasyPaisa Payment.
If you encounter any foreground service permission-related errors when using the XPay SDK, please make sure the following permissions are added in your app’s AndroidManifest.xml file:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />Additionally, for notification-related functionality, you may optionally add:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />Configuring XML Layout
To render SDK-provided UI components in your app, define dedicated ViewGroup containers in your layout XML. Each component must be placed inside its own ViewGroup to ensure proper isolation, styling, and rendering.
Using LinearLayout
Card Payment Element
Create a container ViewGroup for the PaymentElement. You can use any layout type such as LinearLayout, FrameLayout, or ConstraintLayout, depending on your UI design.
Example using LinearLayout:
<LinearLayout
android:id="@+id/paymentElementView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>EasyPaisa Payment Element
Similarly, define a separate ViewGroup to render the EasyPaisaPaymentElement. This helps keep the layout modular and ensures each component renders independently.
Example using LinearLayout:
<LinearLayout
android:id="@+id/easyPaisaElementView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
JazzCash Payment Element
Similarly, define a separate ViewGroup to render the JazzCashPaymentElement. This helps keep the layout modular and ensures each component renders independently.
Example using LinearLayout:
<LinearLayout
android:id="@+id/jazzCashElementView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>Initialize and Add the Payment Elements to ViewGroups
This step involves initializing the SDK UI components — PaymentElement , JazzCashPaymentElement and EasyPaisaPaymentElement — and adding them to their respective ViewGroup containers in your activity or fragment layout.
Each element must be added to a separate ViewGroup (e.g., paymentElementView for PaymentElement, jazzCashElementView for JazzCashPaymentElement and easyPaisaElementView for EasyPaisaPaymentElement) .
Card Payment Element
Kotlin
import com.xstak.xpay_element.PaymentElement
import com.xstak.xpay_element.EasyPaisaPaymentElement
import com.xstak.xpay_element.JazzCashPaymentElement
// card payment
val paymentContainer: ViewGroup = findViewById(R.id.paymentElementView)
val paymentElement = PaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id"
)
paymentContainer.addView(paymentElement)
// easypaisa
val easypaisaContainer: ViewGroup = findViewById(R.id.easyPaisaElementView)
val easyPaisaElement = EasyPaisaPaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id"
)
easypaisaContainer.addView(easyPaisaElement)
// jazzcash
val jazzcashContainer: ViewGroup = findViewById(R.id.jazzCashElementView)
val jazzCashElement = JazzCashPaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id"
)
jazzcashContainer.addView(jazzCashElement)Java
import com.xstak.xpay_element.PaymentElement;
import com.xstak.xpay_element.EasyPaisaPaymentElement;
import com.xstak.xpay_element.JazzCashPaymentElement;
// card payment
ViewGroup paymentContainer = findViewById(R.id.paymentElementView);
PaymentElement paymentElement = new PaymentElement(
this,
"your_public_key",
"your_hmac_secret_key",
"your_account_id"
);
paymentContainer.addView(paymentElement);
// easypaisa
ViewGroup easypaisaContainer = findViewById(R.id.easyPaisaElementView);
EasyPaisaPaymentElement easyPaisaElement = new EasyPaisaPaymentElement(
this,
"your_public_key",
"your_hmac_secret_key",
"your_account_id"
);
easypaisaContainer.addView(easyPaisaElement);
// jazzcash
ViewGroup jazzcashContainer = findViewById(R.id.jazzCashElementView);
JazzCashPaymentElement jazzCashElement = new JazzCashPaymentElement(
this,
"your_public_key",
"your_hmac_secret_key",
"your_account_id"
);
jazzcashContainer.addView(jazzCashElement);Enabling Additional Card Types (AMEX, PayPak)
By default, the PaymentElement supports Visa and Mastercard. If you want to enable additional card types such as AMEX or PayPak, you can pass them using the enabledCardTypes function.
Kotlin
import com.xstak.xpay_element.Card
import com.xstak.xpay_element.PaymentElement
val paymentElement = PaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id"
)
// Enable AMEX and PayPak cards in addition to Visa and Mastercard (default)
paymentElement.enabledCardTypes(Card.AMEX, Card.PAYPAK)Java
import com.xstak.xpay_element.Card;
import com.xstak.xpay_element.PaymentElement;
PaymentElement paymentElement = new PaymentElement(
this,
"your_public_key",
"your_hmac_secret_key",
"your_account_id"
);
// Enable AMEX and PayPak cards in addition to Visa and Mastercard (default)
paymentElement.enabledCardTypes(Card.AMEX, Card.PAYPAK);SDK Events
OnReady:
The onReady event is fired when the payment element is fully initialized and ready to process payments. This allows you to perform actions like enabling the pay button only when the element is ready.
import com.xstak.xpay_element.PaymentElement
import com.xstak.xpay_element.EasyPaisaPaymentElement
import com.xstak.xpay_element.JazzCashPaymentElement
// Initialize PaymentElement and listen for readiness
val paymentElement = PaymentElement(this, publicKey, hmacSecretKey, accountId)
paymentElement.onReady { isSuccess ->
payButton.isEnabled = isSuccess
}
// Initialize EasyPaisaPaymentElement and listen for readiness
val easyPaisaElement = EasyPaisaPaymentElement(this, publicKey, hmacSecretKey, accountId)
easyPaisaElement.onReady { isSuccess ->
payButton.isEnabled = isSuccess
}
// Initialize JazzCashPaymentElement and listen for readiness
val jazzCashElement = JazzCashPaymentElement(this, publicKey, hmacSecretKey, accountId)
jazzCashElement.onReady { isSuccess ->
payButton.isEnabled = isSuccess
}Java
import com.xstak.xpay_element.PaymentElement;
import com.xstak.xpay_element.EasyPaisaPaymentElement;
import com.xstak.xpay_element.JazzCashPaymentElement;
import kotlin.Unit;
// Initialize PaymentElement and listen for readiness
PaymentElement paymentElement = new PaymentElement(this, publicKey, hmacSecretKey, accountId);
paymentElement.onReady(isSuccess -> {
payButton.setEnabled(isSuccess);
return Unit.INSTANCE;
});
// Initialize EasyPaisaPaymentElement and listen for readiness
EasyPaisaPaymentElement easyPaisaElement = new EasyPaisaPaymentElement(this, publicKey, hmacSecretKey, accountId);
easyPaisaElement.onReady(isSuccess -> {
payButton.setEnabled(isSuccess);
return Unit.INSTANCE;
});
// Initialize JazzCashPaymentElement and listen for readiness
JazzCashPaymentElement jazzCashElement = new JazzCashPaymentElement(this, publicKey, hmacSecretKey, accountId);
jazzCashElement.onReady(isSuccess -> {
payButton.setEnabled(isSuccess);
return Unit.INSTANCE;
});OnBinDiscount:
Triggers when a user inputs their card number, and the system checks for discounts based on the BIN.
Kotlin
import com.xstak.xpay_element.PaymentElement
val paymentElement = PaymentElement(
this,
publicKey = publicKey,
hmacSecretKey = hamcSecretKey,
accountId = accountID,
)
paymentElement.onBinDiscount { data ->
Log.d("Bin Discount Data", data)
}Java
import com.xstak.xpay_element.PaymentElement;
import kotlin.Unit;
PaymentElement paymentElement = new PaymentElement(this, publicKey, hmacSecretKey, accountId);
paymentElement.onBinDiscount(data -> {
Log.e("Bin Discount Data", data);
return Unit.INSTANCE;
});Customize the Appearance
You can customize the styles for input fields, labels, focus, and error states to match your app’s design. PaymentElement, JazzCashPaymentElement and EasyPaisaPaymentElement support the same style configuration objects.
You can either define:
- Separate
InputFieldsConfigfor each element, or - A single combined
InputFieldsConfigwith all fields if you plan to use both elements.
Kotlin
import com.xstak.xpay_element.*
// Option 1: Single config for both PaymentElement,JazzCashPaymentElement & EasyPaisaPaymentElement
val configAll = InputFieldsConfig(
creditCardPlaceholder = "0000 0000 0000 0000",
creditCardLabel = "Card Number",
expiryPlaceholder = "MM/YY",
expiryLabel = "Expiry Date",
cvcPlaceholder = "CVC",
cvcLabel = "CVC",
phoneNumberLabel = "Phone Number",
phoneNumberPlaceholder = "0301-2345678",
cnicLabel = "CNIC",
cnicPlaceholder = "Enter last 6 digits of your CNIC"
)
// Option 2: Separate configs
// card payment element
val configPaymentElement = InputFieldsConfig(
creditCardPlaceholder = "0000 0000 0000 0000",
creditCardLabel = "Card Number",
expiryPlaceholder = "MM/YY",
expiryLabel = "Expiry Date",
cvcPlaceholder = "CVC",
cvcLabel = "CVC"
)
// easypaisa
val configEasypaisaElement = InputFieldsConfig(
phoneNumberLabel = "Phone Number",
phoneNumberPlaceholder = "0301-2345678"
)
// jazzcash
val configJazzcashElement = InputFieldsConfig(
phoneNumberLabel = "Phone Number",
phoneNumberPlaceholder = "0301-2345678",
cnicLabel = "CNIC",
cnicPlaceholder = "Enter last 6 digits of your CNIC"
)
// Common styles
val inputFieldStyle = InputFieldsStyle(
borderColor = "#e6e6e6",
borderWidth = 1,
borderRadius = 5,
textSize = 18f,
textColor = "#000000",
placeholderColor = "#B5B5B5"
)
val onFocusFieldStyle = OnFocusInputFieldsStyle(
borderColor = "#C8DBF9",
borderRadius = 5,
borderWidth = 1,
textSize = 18f,
textColor = "#000000"
)
val onErrorInputFieldStyle = InvalidStyle(
borderColor = "#FF0000",
borderRadius = 5,
borderWidth = 1,
inputTextSize = 18f,
inputTextColor = "#FF0000",
errorTextSize = 15f,
errorTextColor = "#FF0000"
)
val labelStyle = LabelStyle(
textColor = "#000000",
textSize = 15f
)
// Initialize PaymentElement
val paymentElement = PaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id",
inputFieldsConfig = configPaymentElement, // or configAll
inputFieldsStyle = inputFieldStyle,
onFocusInputFieldsStyle = onFocusFieldStyle,
invalidStyle = onErrorInputFieldStyle,
labelsStyle = labelStyle
)
// Initialize EasyPaisaPaymentElement
val easyPaisaElement = EasyPaisaPaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id",
inputFieldsConfig = configEasypaisaElement, // or configAll
inputFieldsStyle = inputFieldStyle,
onFocusInputFieldsStyle = onFocusFieldStyle,
invalidStyle = onErrorInputFieldStyle,
labelsStyle = labelStyle
)
// Initialize JazzCashPaymentElement
val easyPaisaElement = JazzCashPaymentElement(
this,
publicKey = "your_public_key",
hmacSecretKey = "your_hmac_secret_key",
accountId = "your_account_id",
inputFieldsConfig = configJazzcashElement, // or configAll
inputFieldsStyle = inputFieldStyle,
onFocusInputFieldsStyle = onFocusFieldStyle,
invalidStyle = onErrorInputFieldStyle,
labelsStyle = labelStyle
)Java
import com.xstak.xpay_element.*;
// Option 1: Single config for both elements
InputFieldsConfig configAll = new InputFieldsConfig(
"0000 0000 0000 0000",
"Card Number",
"MM/YY",
"Expiry Date",
"CVC",
"CVC",
"Phone Number",
"0301-2345678",
"CNIC",
"Enter last 6 digits of your CNIC"
);
// Option 2: Separate configs
// card payment element
InputFieldsConfig configPaymentElement = new InputFieldsConfig(
"0000 0000 0000 0000",
"Card Number",
"MM/YY",
"Expiry Date",
"CVC",
"CVC"
);
// easypaisa
InputFieldsConfig configEasypaisaElement = new InputFieldsConfig(
"Phone Number",
"0301-2345678"
);
// jazzcash
InputFieldsConfig configJazzcashElement = new InputFieldsConfig(
"Phone Number",
"0301-2345678",
"CNIC",
"Enter last 6 digits of your CNIC"
);
// Common styles
InputFieldsStyle inputFieldStyle = new InputFieldsStyle(
"#e6e6e6", 1, 5, 18f, "#000000", "#B5B5B5"
);
OnFocusInputFieldsStyle onFocusFieldStyle = new OnFocusInputFieldsStyle(
"#C8DBF9", 5, 1, 18f, "#000000"
);
InvalidStyle onErrorInputFieldStyle = new InvalidStyle(
"#FF0000", 5, 1, 18f, "#FF0000", 15f, "#FF0000"
);
LabelStyle labelStyle = new LabelStyle("#000000", 15f);
// Initialize PaymentElement
PaymentElement paymentElement = new PaymentElement(
this,
publicKey,
hmacSecretKey,
accountId,
configPaymentElement, // or configAll
inputFieldStyle,
onFocusFieldStyle,
labelStyle,
onErrorInputFieldStyle
);
// Initialize EasyPaisaPaymentElement
EasyPaisaPaymentElement easyPaisaElement = new EasyPaisaPaymentElement(
this,
publicKey,
hmacSecretKey,
accountId,
configEasypaisaElement, // or configAll
inputFieldStyle,
onFocusFieldStyle,
labelStyle,
onErrorInputFieldStyle
);
// Initialize JazzCashPaymentElement
JazzCashPaymentElement jazzCashElement = new JazzCashPaymentElement(
this,
publicKey,
hmacSecretKey,
accountId,
configJazzcashElement, // or configAll
inputFieldStyle,
onFocusFieldStyle,
labelStyle,
onErrorInputFieldStyle
);Clear Method
The clear() method resets the input fields of the payment form. It's useful when the user wants to start over or reset the form during the checkout process.
You can call clear() on both PaymentElement, JazzCashPaymentElement and EasyPaisaPaymentElement.
Kotlin
import com.xstak.xpay_element.PaymentElement
import com.xstak.xpay_element.EasyPaisaPaymentElement
import com.xstak.xpay_element.JazzCashPaymentElement
// Initialize PaymentElement
val paymentElement = PaymentElement(
this,
publicKey = publicKey,
hmacSecretKey = hmacSecretKey,
accountId = accountId
)
// Initialize EasyPaisaPaymentElement
val easyPaisaElement = EasyPaisaPaymentElement(
this,
publicKey = publicKey,
hmacSecretKey = hmacSecretKey,
accountId = accountId
)
// Initialize JazzCashPaymentElement
val jazzCashElement = JazzCashPaymentElement(
this,
publicKey = publicKey,
hmacSecretKey = hmacSecretKey,
accountId = accountId
)
// Clear button click listener
clearButton.setOnClickListener {
paymentElement.clear() // Clear credit card input fields
easyPaisaElement.clear() // Clear Easypaisa input fields
jazzCashElement.clear() // Clear Jazzcash input fields
}Java
import com.xstak.xpay_element.PaymentElement;
import com.xstak.xpay_element.EasyPaisaPaymentElement;
import com.xstak.xpay_element.JazzCashPaymentElement
// Initialize PaymentElement
PaymentElement paymentElement = new PaymentElement(this, publicKey, hmacSecretKey, accountId);
// Initialize EasyPaisaPaymentElement
EasyPaisaPaymentElement easyPaisaElement = new EasyPaisaPaymentElement(this, publicKey, hmacSecretKey, accountId);
// Initialize JazzCashPaymentElement
JazzCashPaymentElement jazzCashElement = new JazzCashPaymentElement(this, publicKey, hmacSecretKey, accountId);
// Clear button click listener
clearButton.setOnClickListener(v -> {
paymentElement.clear(); // Clear credit card input fields
easyPaisaElement.clear(); // Clear Easypaisa input fields
jazzCashElement.clear(); // Clear Jazzcash input fields
});Payments Confirmation Method
The confirmPayment method is used to submit the payment details to payment gateway. This method must be called after the payment intent has been created on your server, which provides necessary details such as the pi_client_secret and encryptionKeys. These details are crucial for securely processing the payment.
Parameters Explained:
CustomerName: Represents the name of the customer making the transaction.pi_client_secret: This is a key generated by your payment intent API on the server. It is used to securely identify and authorize the payment transaction.encryptionKeys: These keys are used to encrypt and secure the payment data, ensuring that sensitive information is protected throughout the transaction process.callback: This function is executed once the payment processor returns a response. It is essential for handling the results of the payment attempt, allowing your application to react accordingly (e.g., updating UI, notifying the user).
⚠️ Use the appropriate method (PaymentElement, JazzCashPaymentElement or EasyPaisaPaymentElement) based on the selected payment method.
Kotlin
import com.xstak.xpay_element.PaymentElement
import com.xstak.xpay_element.EasyPaisaPaymentElement
import com.xstak.xpay_element.JazzCashPaymentElement
// Initialize elements
val paymentElement = PaymentElement(this, publicKey, hmacSecretKey, accountId)
val easyPaisaElement = EasyPaisaPaymentElement(this, publicKey, hmacSecretKey, accountId)
val jazzCashElement = JazzCashPaymentElement(this, publicKey, hmacSecretKey, accountId)
// Call this based on selected payment method
val selectedPaymentMethod = "card" // or "easypaisa"
if (selectedPaymentMethod == "card") {
paymentElement.confirmPayment(
"Test User",
piClientSecret,
encryptionKeys
) { response -> handlePaymentResponse(response) }
} else if (selectedPaymentMethod == "easypaisa") {
easyPaisaElement.confirmPayment(
"Test User",
piClientSecret,
encryptionKeys
) { response -> handlePaymentResponse(response) }
} else if (selectedPaymentMethod == "jazzcash") {
jazzCashElement.confirmPayment(
"Test User",
piClientSecret,
encryptionKeys
) { response -> handlePaymentResponse(response) }
}
// Response handler
fun handlePaymentResponse(response: String) {
Log.d("Payment Response", response)
}Java
import com.xstak.xpay_element.PaymentElement;
import com.xstak.xpay_element.EasyPaisaPaymentElement;
import com.xstak.xpay_element.JazzCashPaymentElement
import kotlin.Unit;
// Initialize elements
PaymentElement paymentElement = new PaymentElement(this, publicKey, hmacSecretKey, accountId);
EasyPaisaPaymentElement easyPaisaElement = new EasyPaisaPaymentElement(this, publicKey, hmacSecretKey, accountId);
JazzCashPaymentElement jazzCashElement = new JazzCashPaymentElement(this, publicKey, hmacSecretKey, accountId);
// Call based on selected payment method
String selectedPaymentMethod = "card"; // or "easypaisa"
if (selectedPaymentMethod.equals("card")) {
paymentElement.confirmPayment("Test User", piClientSecret, encryptionKeys, this::handlePaymentResponse);
} else if (selectedPaymentMethod.equals("easypaisa")) {
easyPaisaElement.confirmPayment("Test User", piClientSecret, encryptionKeys, this::handlePaymentResponse);
} else if (selectedPaymentMethod.equals("jazzcash")) {
jazzCashElement.confirmPayment("Test User", piClientSecret, encryptionKeys, this::handlePaymentResponse);
}
// Response handler
private Unit handlePaymentResponse(String response) {
Log.e("Payment Response Data", response);
return Unit.INSTANCE;
}Response Structure:
The callback receives a JSON string containing:
error: A boolean indicating whether the transaction failed (true) or succeeded (false).message: A text description providing more details about the outcome of the transaction.