The xMoney Android SDK provides a drop-in Payment Sheet, an embeddable Payment Element, and standalone Google Pay. All surfaces use PaymentConfig, PaymentIntent, and PaymentResult.
Current release: 1.0.0.
- Android Gradle Plugin 8.5 or later
- Kotlin 2.0 or later
- Java 17
minSdk23 andcompileSdk36- A host screen based on
FragmentActivity - A publishable xMoney key and a backend that signs orders
Store the xMoney private key on your backend. The app should receive only the publishable key, order payload, and checksum.
Make sure mavenCentral() is configured, then add Payment Sheet:
dependencies {
implementation("com.xmoney:paymentsheet:1.0.0")
}Payment Sheet brings in Payment Element and Google Pay at runtime. To choose surfaces separately:
dependencies {
implementation("com.xmoney:paymentelement:1.0.0")
implementation("com.xmoney:googlepay:1.0.0")
}Payment Element does not include the Google Pay module automatically.
Your backend creates and signs the order. Build an intent from the returned values:
val intent = PaymentIntent(
OrderPayload(payloadFromYourServer),
OrderChecksum(checksumFromYourServer),
)See Create the order on your backend for the shared server responsibilities.
Payment Sheet owns the complete checkout UI and Pay button.
val sheet = rememberPaymentSheet(
configuration = PaymentConfig(
publicKey = "pk_test_...",
paymentMethods = PaymentMethodsConfig(
googlePay = GooglePayConfig(enabled = true),
),
card = CardConfig(
savedCards = SavedCardsConfig(enabled = true),
),
),
onResult = ::handlePaymentResult,
)
sheet.present(intent) { event ->
if (event is PaymentSheetEvent.Ready) {
hideCheckoutLoader()
}
}Idle close, back, drag, or scrim dismissal returns Canceled without consuming the order. Once payment or 3DS starts, terminal results consume the signed order.
Payment Element places the form in your Compose layout. Keep it composed while it binds the order.
var ready by remember { mutableStateOf(false) }
val embedded = rememberEmbeddedPayment(
configuration = configuration,
onResult = ::handlePaymentResult,
)
if (!ready) {
CheckoutLoader()
}
PaymentElement(
controller = embedded,
intent = intent,
modifier = Modifier.padding(horizontal = 20.dp),
onEvent = { event ->
if (event is EmbeddedEvent.Ready) {
ready = true
}
},
)Payment Element supplies no outer page padding or background. Keep it mounted, and apply spacing and surrounding layout in your app.
To use your own Pay button, hide the SDK button and call confirm() after Ready:
val configuration = PaymentConfig(
publicKey = "pk_test_...",
card = CardConfig(
submitButton = SubmitButtonConfig(visible = false),
),
)
Button(
onClick = { embedded.confirm() },
enabled = embedded.isInteractionEnabled,
) {
Text("Pay")
}val googlePay = rememberGooglePay(
configuration = configuration,
onResult = ::handlePaymentResult,
)
GooglePayButton(
controller = googlePay,
intent = intent,
onEvent = { event ->
if (event is GooglePayEvent.Ready) {
hideWalletLoader()
}
},
)Use isAvailable, isReady, isInteractionEnabled, and isOrderConsumed to control surrounding wallet UI. For Activity integrations, call GooglePay.availability(activity, intent) before present.
A wallet dismissal before authorization returns Canceled and does not consume the intent.
See Google Pay for activation and integration options.
fun handlePaymentResult(result: PaymentResult) {
when (result) {
is PaymentResult.Complete -> confirmOrderOnBackend(result.transaction)
is PaymentResult.Failed -> showPaymentError(result.error.message)
PaymentResult.Canceled -> showCheckout()
}
}Use the result to update the app, then confirm final payment status on your backend before fulfillment.
When the amount, currency, or cart changes, request a new signed order and rebind the mounted element:
embedded.updateOrder(
PaymentIntent(
OrderPayload(nextPayload),
OrderChecksum(nextChecksum),
),
)Changing the intent passed to PaymentElement also updates the order. Do not unmount the element for a loader. isInteractionEnabled remains false until the new intent is ready.
For live styling changes, keep the controller and call updateAppearance, updateStyle, updateLocale, or updateWalletAppearance.
PaymentConfig controls saved cards, card validation, field grouping, submit-button intent, locale, light/dark style, colors, and radii.
val configuration = PaymentConfig(
publicKey = "pk_test_...",
card = CardConfig(
savedCards = SavedCardsConfig(
enabled = true,
optInVisible = true,
),
validationMode = ValidationMode.ON_TOUCHED,
inputs = CardInputsConfig(
grouping = CardGrouping.CONDENSED,
),
),
options = OptionsConfig(
locale = "en-US",
style = UserInterfaceStyle.AUTOMATIC,
),
)Supported UI languages are English (en), Greek (el), Romanian (ro), Bulgarian (bg), Hungarian (hu), and Polish (pl). Region tags such as pl-PL are supported.
For cardholder name verification, provide CardHolderVerification and return true to continue:
card = CardConfig(
cardHolderVerification = CardHolderVerification(
name = CardHolderName(
firstName = "John",
lastName = "Doe",
),
onCardHolderVerification = { result ->
result.status == CardHolderMatchStatus.MATCHED
},
),
)The site must have cardholder name verification enabled.
- Review the native SDK shared model.
- Use test cards in the test environment.
- Explore the complete Android SDK samples.