Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
## Changelog

### Unreleased

This release only adds APIs and fields. No existing signature, type or value changes.

* Add `SettingsManager.getSdkSettings()`, which returns a new `SdkSettings` object (`sdkVersion`, `sdkEnvironment`, `securityComplianceVersion`)
* Add `SettingsManager.getTrackingConsentState()` and `SettingsManager.updateTrackingConsent(granted)`, with a new `TrackingConsentState` enum
* Add `PaymentManager.getIdempotencyKey(paymentAttemptId)`, which returns the idempotency key the SDK generated for a payment attempt
* Add `PaymentManager.isPaymentInProgress()`
* Add `PaymentManager.getAvailableCardEntryMethods()` and `PaymentManager.setAvailableCardEntryMethodsCallback(callback)`, to know which card entry methods the connected hardware accepts and be notified when they change
* **Android:** Add `PaymentManager.completePayment(paymentId)` to capture a payment taken with `autocomplete: false`. It takes the server-assigned `Payment.id`, not the `paymentAttemptId`
* **Android:** Add `PaymentManager.getAllIdempotencyKeys()`, which returns a list of the new `IdempotencyKeyData` object
* Add `ReaderManager.retryConnection(id)`, which returns a new `RetryConnectionResult` enum, and `ReaderManager.rebootReader(id)` (iOS only)
* Add `ReaderManager.setPreferredFirmwareUpdateTime(time)` and `ReaderManager.setReducedChargingModeEnabled(enabled)`, so `ReaderSettings` can be changed and not only read
* Payments now include `status`, `cardDetails`, `customerId`, `note`, `teamMemberId`, `cashDetails` and a `paymentType` of `ONLINE` or `OFFLINE`. Both platforms previously returned only the base fields. New `CashPaymentDetails`, `CardSurchargeDetails`, `Capabilities` and `PaymentProcessingFee` objects, and `PaymentType`, `PaymentCapability`, `PaymentProcessingFeeType`, `VerificationMethod` and `VerificationResult` enums
* **Android:** Online payments also include `receiptNumber`, `receiptUrl`, `statementDescription`, `capabilities` and `processingFee`, and card details include `verificationMethod`, `verificationResults` and `Card.bin`
* **iOS:** `OfflinePaymentQueue.getPayments()` now includes `localId`, `status`, `uploadedAt` and `cardDetails`
* `ReaderInfo` now includes `connectionType` and, on iOS, `isRebootable`. `ReaderFirmwareInfo` includes `updateStatus` and `updateTime`, and the reader status includes `title` and `detail` on iOS. New `ReaderConnectionType` and `FirmwareUpdateStatus` enums
* **Android:** `Location` now includes `merchantId`, `businessName` and `cardProcessingActivated`

### v2026.8.1 Aug 24, 2026

* Upgrade Android native SDK to `2.6.1`, which fixes a bug where Tap to Pay on Android became unavailable for up to 2 hours before the daily payment-key rotation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class MobilePaymentsSdkReactNativeModule(private val reactContext: ReactApplicat
private var paymentHandle: PaymentHandle? = null
private var authStateCallback: CallbackReference? = null
private var readerChangedCallbacks = mutableMapOf<String, CallbackReference>();
private var cardEntryMethodsCallbacks = mutableMapOf<String, CallbackReference>();
private var pairingHandler: PairingHandle? = null

override fun getName(): String {
Expand Down Expand Up @@ -143,6 +144,25 @@ class MobilePaymentsSdkReactNativeModule(private val reactContext: ReactApplicat
}
}

@ReactMethod
fun getSdkSettings(promise: Promise) {
val settingsManager = MobilePaymentsSdk.settingsManager()
promise.resolve(settingsManager.getSdkSettings().toSdkSettingsMap())
}

@ReactMethod
fun getTrackingConsentState(promise: Promise) {
val settingsManager = MobilePaymentsSdk.settingsManager()
promise.resolve(settingsManager.trackingConsentState.toTrackingConsentStateString())
}

@ReactMethod
fun updateTrackingConsent(granted: Boolean, promise: Promise) {
val settingsManager = MobilePaymentsSdk.settingsManager()
settingsManager.updateTrackingConsent(granted)
promise.resolve(null)
}

@ReactMethod
fun showMockReaderUI(promise: Promise) {
if (!MobilePaymentsSdk.isSandboxEnvironment()) {
Expand Down Expand Up @@ -278,6 +298,88 @@ class MobilePaymentsSdkReactNativeModule(private val reactContext: ReactApplicat
}
}

@ReactMethod
fun getIdempotencyKey(paymentAttemptId: String, promise: Promise) {
val paymentManager = MobilePaymentsSdk.paymentManager()
when (val result = paymentManager.getIdempotencyKey(paymentAttemptId)) {
is Success -> promise.resolve(result.value)
is Failure ->
promise.reject("GET_IDEMPOTENCY_KEY_FAILED", result.errorMessage, result.toErrorMap())
}
}

@ReactMethod
fun getAllIdempotencyKeys(promise: Promise) {
reactContext.runOnUiQueueThread {
val paymentManager = MobilePaymentsSdk.paymentManager()
paymentManager.getAllIdempotencyKeys { result ->
when (result) {
is Success -> {
val keys = Arguments.createArray()
result.value.forEach { keys.pushMap(it.toIdempotencyKeyDataMap()) }
promise.resolve(keys)
}
is Failure ->
promise.reject("GET_IDEMPOTENCY_KEYS_FAILED", result.errorMessage, result.toErrorMap())
}
}
}
}

@ReactMethod
fun completePayment(paymentId: String, promise: Promise) {
reactContext.runOnUiQueueThread {
val paymentManager = MobilePaymentsSdk.paymentManager()
paymentManager.completePayment(paymentId) { result ->
when (result) {
is Success -> promise.resolve(result.value.toPaymentMap())
is Failure ->
promise.reject("COMPLETE_PAYMENT_FAILED", result.errorMessage, result.toErrorMap())
}
}
}
}

@ReactMethod
fun isPaymentInProgress(promise: Promise) {
reactContext.runOnUiQueueThread {
val paymentManager = MobilePaymentsSdk.paymentManager()
promise.resolve(paymentManager.currentPaymentHandle != null)
}
}

@ReactMethod
fun getAvailableCardEntryMethods(promise: Promise) {
val paymentManager = MobilePaymentsSdk.paymentManager()
promise.resolve(paymentManager.getAvailableCardEntryMethods().toCardEntryMethodsArray())
}

@ReactMethod
fun addAvailableCardEntryMethodsCallback(refId: String, promise: Promise) {
val paymentManager = MobilePaymentsSdk.paymentManager()
val ref = paymentManager.setAvailableCardEntryMethodChangedCallback { methods ->
emitEvent(
reactContext,
"AvailableCardEntryMethodsChanged-${refId}",
WritableNativeMap().apply {
putArray("methods", methods.toCardEntryMethodsArray())
}
)
}
cardEntryMethodsCallbacks.put(refId, ref)
promise.resolve(refId)
}

@ReactMethod
fun removeAvailableCardEntryMethodsCallback(refId: String, promise: Promise) {
val ref = cardEntryMethodsCallbacks.get(refId)
if (ref != null) {
ref.clear()
cardEntryMethodsCallbacks.remove(refId)
}
promise.resolve(null)
}

@ReactMethod
fun getReaders(promise: Promise) {
val readerManager = MobilePaymentsSdk.readerManager()
Expand Down Expand Up @@ -344,6 +446,26 @@ class MobilePaymentsSdkReactNativeModule(private val reactContext: ReactApplicat
}
// ---

@ReactMethod
fun setPreferredFirmwareUpdateTime(time: ReadableMap?, promise: Promise) {
val timeOfDay = try {
time?.toTimeOfDay()
} catch (e: IllegalArgumentException) {
promise.reject("INVALID_TIME_OF_DAY", "hour and minute are required and must form a valid time of day", e)
return
}
val readerManager = MobilePaymentsSdk.readerManager()
readerManager.readerSettings.preferredFirmwareUpdateTime = timeOfDay
promise.resolve(null)
}

@ReactMethod
fun setReducedChargingModeEnabled(enabled: Boolean, promise: Promise) {
val readerManager = MobilePaymentsSdk.readerManager()
readerManager.readerSettings.isReducedChargingModeEnabled = enabled
promise.resolve(null)
}

@ReactMethod
fun retryConnection(id: String, promise: Promise) {
val readerManager = MobilePaymentsSdk.readerManager()
Expand Down
Loading
Loading