@gmisoftware/react-native-pay 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +223 -220
- package/android/src/main/java/com/margelo/nitro/pay/GooglePayRequestBuilder.kt +45 -12
- package/android/src/main/java/com/margelo/nitro/pay/HybridPaymentHandler.kt +27 -8
- package/ios/HybridPaymentHandler.swift +116 -9
- package/lib/hooks/__tests__/usePaymentCheckout.integration.test.d.ts +1 -0
- package/lib/hooks/__tests__/usePaymentCheckout.integration.test.js +191 -0
- package/lib/hooks/usePaymentCheckout.d.ts +47 -3
- package/lib/hooks/usePaymentCheckout.js +6 -4
- package/lib/plugin/__tests__/index.test.d.ts +1 -0
- package/lib/plugin/__tests__/index.test.js +33 -0
- package/lib/plugin/__tests__/withApplePay.test.d.ts +1 -0
- package/lib/plugin/__tests__/withApplePay.test.js +58 -0
- package/lib/plugin/__tests__/withGooglePay.test.d.ts +1 -0
- package/lib/plugin/__tests__/withGooglePay.test.js +45 -0
- package/lib/plugin/withApplePay.d.ts +1 -0
- package/lib/plugin/withApplePay.js +19 -4
- package/lib/types/Payment.d.ts +2 -1
- package/lib/utils/__tests__/paymentHelpers.test.d.ts +1 -0
- package/lib/utils/__tests__/paymentHelpers.test.js +75 -0
- package/lib/utils/paymentHelpers.d.ts +1 -4
- package/lib/utils/paymentHelpers.js +2 -5
- package/nitrogen/generated/android/NitroPay+autolinking.cmake +1 -1
- package/nitrogen/generated/android/NitroPay+autolinking.gradle +1 -1
- package/nitrogen/generated/android/NitroPayOnLoad.cpp +1 -1
- package/nitrogen/generated/android/NitroPayOnLoad.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNContact.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNContactType.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNLabeledEmailAddress.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNLabeledPhoneNumber.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNLabeledPostalAddress.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNPhoneNumber.hpp +1 -1
- package/nitrogen/generated/android/c++/JCNPostalAddress.hpp +1 -1
- package/nitrogen/generated/android/c++/JFunc_void.hpp +1 -1
- package/nitrogen/generated/android/c++/JGooglePayButtonTheme.hpp +1 -1
- package/nitrogen/generated/android/c++/JGooglePayButtonType.hpp +1 -1
- package/nitrogen/generated/android/c++/JGooglePayEnvironment.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridGooglePayButtonSpec.cpp +1 -1
- package/nitrogen/generated/android/c++/JHybridGooglePayButtonSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridPaymentHandlerSpec.cpp +1 -1
- package/nitrogen/generated/android/c++/JHybridPaymentHandlerSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JPKSecureElementPass.hpp +1 -1
- package/nitrogen/generated/android/c++/JPassActivationState.hpp +1 -1
- package/nitrogen/generated/android/c++/JPayServiceStatus.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentItem.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentItemType.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentMethod.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentMethodType.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentNetwork.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentRequest.hpp +10 -6
- package/nitrogen/generated/android/c++/JPaymentResult.hpp +1 -1
- package/nitrogen/generated/android/c++/JPaymentToken.hpp +1 -1
- package/nitrogen/generated/android/c++/views/JHybridGooglePayButtonStateUpdater.cpp +1 -1
- package/nitrogen/generated/android/c++/views/JHybridGooglePayButtonStateUpdater.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNContact.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNContactType.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNLabeledEmailAddress.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNLabeledPhoneNumber.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNLabeledPostalAddress.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNPhoneNumber.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/CNPostalAddress.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/Func_void.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/GooglePayButtonTheme.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/GooglePayButtonType.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/GooglePayEnvironment.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/HybridGooglePayButtonSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/HybridPaymentHandlerSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/NitroPayOnLoad.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PKSecureElementPass.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PassActivationState.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PayServiceStatus.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentItem.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentItemType.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentMethod.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentMethodType.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentNetwork.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentRequest.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentResult.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/PaymentToken.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/views/HybridGooglePayButtonManager.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pay/views/HybridGooglePayButtonStateUpdater.kt +1 -1
- package/nitrogen/generated/ios/NitroPay+autolinking.rb +1 -1
- package/nitrogen/generated/ios/NitroPay-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/NitroPay-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/NitroPay-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroPayAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroPayAutolinking.swift +1 -1
- package/nitrogen/generated/ios/c++/HybridApplePayButtonSpecSwift.cpp +1 -1
- package/nitrogen/generated/ios/c++/HybridApplePayButtonSpecSwift.hpp +1 -1
- package/nitrogen/generated/ios/c++/HybridPaymentHandlerSpecSwift.cpp +1 -1
- package/nitrogen/generated/ios/c++/HybridPaymentHandlerSpecSwift.hpp +1 -1
- package/nitrogen/generated/ios/c++/views/HybridApplePayButtonComponent.mm +1 -1
- package/nitrogen/generated/ios/swift/ApplePayButtonStyle.swift +1 -1
- package/nitrogen/generated/ios/swift/ApplePayButtonType.swift +1 -1
- package/nitrogen/generated/ios/swift/CNContact.swift +1 -1
- package/nitrogen/generated/ios/swift/CNContactType.swift +1 -1
- package/nitrogen/generated/ios/swift/CNLabeledEmailAddress.swift +1 -1
- package/nitrogen/generated/ios/swift/CNLabeledPhoneNumber.swift +1 -1
- package/nitrogen/generated/ios/swift/CNLabeledPostalAddress.swift +1 -1
- package/nitrogen/generated/ios/swift/CNPhoneNumber.swift +1 -1
- package/nitrogen/generated/ios/swift/CNPostalAddress.swift +1 -1
- package/nitrogen/generated/ios/swift/Func_void.swift +1 -1
- package/nitrogen/generated/ios/swift/Func_void_PaymentResult.swift +1 -1
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -1
- package/nitrogen/generated/ios/swift/GooglePayEnvironment.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridApplePayButtonSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridApplePayButtonSpec_cxx.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridPaymentHandlerSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridPaymentHandlerSpec_cxx.swift +1 -1
- package/nitrogen/generated/ios/swift/PKSecureElementPass.swift +1 -1
- package/nitrogen/generated/ios/swift/PassActivationState.swift +1 -1
- package/nitrogen/generated/ios/swift/PayServiceStatus.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentItem.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentItemType.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentMethod.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentMethodType.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentNetwork.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentRequest.swift +55 -6
- package/nitrogen/generated/ios/swift/PaymentResult.swift +1 -1
- package/nitrogen/generated/ios/swift/PaymentToken.swift +1 -1
- package/nitrogen/generated/shared/c++/ApplePayButtonStyle.hpp +1 -1
- package/nitrogen/generated/shared/c++/ApplePayButtonType.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNContact.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNContactType.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNLabeledEmailAddress.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNLabeledPhoneNumber.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNLabeledPostalAddress.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNPhoneNumber.hpp +1 -1
- package/nitrogen/generated/shared/c++/CNPostalAddress.hpp +1 -1
- package/nitrogen/generated/shared/c++/GooglePayButtonTheme.hpp +1 -1
- package/nitrogen/generated/shared/c++/GooglePayButtonType.hpp +1 -1
- package/nitrogen/generated/shared/c++/GooglePayEnvironment.hpp +1 -1
- package/nitrogen/generated/shared/c++/HybridApplePayButtonSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridApplePayButtonSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/HybridGooglePayButtonSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridGooglePayButtonSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/HybridPaymentHandlerSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridPaymentHandlerSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/PKSecureElementPass.hpp +1 -1
- package/nitrogen/generated/shared/c++/PassActivationState.hpp +1 -1
- package/nitrogen/generated/shared/c++/PayServiceStatus.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentItem.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentItemType.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentMethod.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentMethodType.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentNetwork.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentRequest.hpp +10 -6
- package/nitrogen/generated/shared/c++/PaymentResult.hpp +1 -1
- package/nitrogen/generated/shared/c++/PaymentToken.hpp +1 -1
- package/nitrogen/generated/shared/c++/views/HybridApplePayButtonComponent.cpp +1 -1
- package/nitrogen/generated/shared/c++/views/HybridApplePayButtonComponent.hpp +1 -1
- package/nitrogen/generated/shared/c++/views/HybridGooglePayButtonComponent.cpp +1 -1
- package/nitrogen/generated/shared/c++/views/HybridGooglePayButtonComponent.hpp +1 -1
- package/package.json +21 -4
- package/src/hooks/__tests__/usePaymentCheckout.integration.test.ts +248 -0
- package/src/hooks/usePaymentCheckout.ts +68 -9
- package/src/plugin/__tests__/index.test.ts +37 -0
- package/src/plugin/__tests__/withApplePay.test.ts +83 -0
- package/src/plugin/__tests__/withGooglePay.test.ts +66 -0
- package/src/plugin/withApplePay.ts +34 -6
- package/src/types/Payment.ts +4 -1
- package/src/utils/__tests__/paymentHelpers.test.ts +127 -0
- package/src/utils/paymentHelpers.ts +30 -15
|
@@ -3,22 +3,27 @@ package com.margelo.nitro.pay
|
|
|
3
3
|
import com.google.android.gms.wallet.WalletConstants
|
|
4
4
|
import org.json.JSONArray
|
|
5
5
|
import org.json.JSONObject
|
|
6
|
+
import java.util.Locale
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Builder for Google Pay API request objects
|
|
9
10
|
*/
|
|
10
11
|
object GooglePayRequestBuilder {
|
|
12
|
+
private val googlePayPriceLocale = Locale.US
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Creates an IsReadyToPay request
|
|
14
16
|
*/
|
|
15
|
-
fun createIsReadyToPayRequest(): JSONObject {
|
|
17
|
+
fun createIsReadyToPayRequest(existingPaymentMethodRequired: Boolean? = null): JSONObject {
|
|
16
18
|
return JSONObject().apply {
|
|
17
19
|
put("apiVersion", PaymentConstants.API_VERSION)
|
|
18
20
|
put("apiVersionMinor", PaymentConstants.API_VERSION_MINOR)
|
|
19
21
|
put("allowedPaymentMethods", JSONArray().apply {
|
|
20
22
|
put(createBaseCardPaymentMethod())
|
|
21
23
|
})
|
|
24
|
+
existingPaymentMethodRequired?.let {
|
|
25
|
+
put("existingPaymentMethodRequired", it)
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
29
|
|
|
@@ -29,6 +34,8 @@ object GooglePayRequestBuilder {
|
|
|
29
34
|
request: PaymentRequest,
|
|
30
35
|
environment: Int
|
|
31
36
|
): JSONObject {
|
|
37
|
+
validatePaymentRequest(request, environment)
|
|
38
|
+
|
|
32
39
|
return JSONObject().apply {
|
|
33
40
|
put("apiVersion", PaymentConstants.API_VERSION)
|
|
34
41
|
put("apiVersionMinor", PaymentConstants.API_VERSION_MINOR)
|
|
@@ -51,7 +58,7 @@ object GooglePayRequestBuilder {
|
|
|
51
58
|
put("merchantName", request.merchantName ?: PaymentConstants.DEFAULT_MERCHANT_NAME)
|
|
52
59
|
// Add merchant ID only for PRODUCTION environment
|
|
53
60
|
if (environment == WalletConstants.ENVIRONMENT_PRODUCTION) {
|
|
54
|
-
put("merchantId", request.
|
|
61
|
+
put("merchantId", request.googlePayMerchantId)
|
|
55
62
|
}
|
|
56
63
|
}
|
|
57
64
|
}
|
|
@@ -88,20 +95,42 @@ object GooglePayRequestBuilder {
|
|
|
88
95
|
request: PaymentRequest,
|
|
89
96
|
environment: Int
|
|
90
97
|
): JSONObject {
|
|
98
|
+
val isProduction = environment == WalletConstants.ENVIRONMENT_PRODUCTION
|
|
99
|
+
val gateway = if (isProduction) {
|
|
100
|
+
request.googlePayGateway!!.trim()
|
|
101
|
+
} else {
|
|
102
|
+
request.googlePayGateway?.trim().takeUnless { it.isNullOrEmpty() }
|
|
103
|
+
?: PaymentConstants.DEFAULT_GATEWAY
|
|
104
|
+
}
|
|
105
|
+
val gatewayMerchantId = if (isProduction) {
|
|
106
|
+
request.googlePayGatewayMerchantId!!.trim()
|
|
107
|
+
} else {
|
|
108
|
+
request.googlePayGatewayMerchantId?.trim().takeUnless { it.isNullOrEmpty() }
|
|
109
|
+
?: PaymentConstants.DEFAULT_GATEWAY_MERCHANT_ID
|
|
110
|
+
}
|
|
111
|
+
|
|
91
112
|
return JSONObject().apply {
|
|
92
113
|
put("type", PaymentConstants.TOKENIZATION_PAYMENT_GATEWAY)
|
|
93
114
|
put("parameters", JSONObject().apply {
|
|
94
|
-
|
|
95
|
-
put("
|
|
96
|
-
put(
|
|
97
|
-
"gatewayMerchantId",
|
|
98
|
-
request.googlePayGatewayMerchantId
|
|
99
|
-
?: if (isProduction) request.merchantIdentifier
|
|
100
|
-
else PaymentConstants.DEFAULT_GATEWAY_MERCHANT_ID
|
|
101
|
-
)
|
|
115
|
+
put("gateway", gateway)
|
|
116
|
+
put("gatewayMerchantId", gatewayMerchantId)
|
|
102
117
|
})
|
|
103
118
|
}
|
|
104
119
|
}
|
|
120
|
+
|
|
121
|
+
private fun validatePaymentRequest(request: PaymentRequest, environment: Int) {
|
|
122
|
+
if (environment != WalletConstants.ENVIRONMENT_PRODUCTION) return
|
|
123
|
+
|
|
124
|
+
require(!request.googlePayMerchantId.isNullOrBlank()) {
|
|
125
|
+
"googlePayMerchantId is required in PRODUCTION"
|
|
126
|
+
}
|
|
127
|
+
require(!request.googlePayGateway.isNullOrBlank()) {
|
|
128
|
+
"googlePayGateway is required in PRODUCTION"
|
|
129
|
+
}
|
|
130
|
+
require(!request.googlePayGatewayMerchantId.isNullOrBlank()) {
|
|
131
|
+
"googlePayGatewayMerchantId is required in PRODUCTION"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
105
134
|
|
|
106
135
|
/**
|
|
107
136
|
* Creates transaction info
|
|
@@ -111,7 +140,7 @@ object GooglePayRequestBuilder {
|
|
|
111
140
|
|
|
112
141
|
return JSONObject().apply {
|
|
113
142
|
put("totalPriceStatus", PaymentConstants.TOTAL_PRICE_STATUS_FINAL)
|
|
114
|
-
put("totalPrice",
|
|
143
|
+
put("totalPrice", formatPrice(totalAmount))
|
|
115
144
|
put("totalPriceLabel", PaymentConstants.TOTAL_PRICE_LABEL_DEFAULT)
|
|
116
145
|
put("currencyCode", request.currencyCode)
|
|
117
146
|
put("countryCode", request.countryCode)
|
|
@@ -138,11 +167,15 @@ object GooglePayRequestBuilder {
|
|
|
138
167
|
else
|
|
139
168
|
PaymentConstants.PENDING_TYPE
|
|
140
169
|
)
|
|
141
|
-
put("price",
|
|
170
|
+
put("price", formatPrice(item.amount))
|
|
142
171
|
})
|
|
143
172
|
}
|
|
144
173
|
}
|
|
145
174
|
}
|
|
175
|
+
|
|
176
|
+
private fun formatPrice(amount: Double): String {
|
|
177
|
+
return String.format(googlePayPriceLocale, "%.2f", amount)
|
|
178
|
+
}
|
|
146
179
|
|
|
147
180
|
/**
|
|
148
181
|
* Creates allowed auth methods
|
|
@@ -5,6 +5,7 @@ import android.content.Intent
|
|
|
5
5
|
import android.util.Log
|
|
6
6
|
import com.facebook.react.bridge.ActivityEventListener
|
|
7
7
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.google.android.gms.tasks.Tasks
|
|
8
9
|
import com.google.android.gms.wallet.AutoResolveHelper
|
|
9
10
|
import com.google.android.gms.wallet.IsReadyToPayRequest
|
|
10
11
|
import com.google.android.gms.wallet.PaymentData
|
|
@@ -14,6 +15,7 @@ import com.google.android.gms.wallet.Wallet
|
|
|
14
15
|
import com.google.android.gms.wallet.WalletConstants
|
|
15
16
|
import com.margelo.nitro.core.Promise
|
|
16
17
|
import com.margelo.nitro.NitroModules
|
|
18
|
+
import java.util.concurrent.TimeUnit
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Hybrid implementation of PaymentHandler for Google Pay on Android
|
|
@@ -38,15 +40,23 @@ class HybridPaymentHandler : HybridPaymentHandlerSpec(), ActivityEventListener {
|
|
|
38
40
|
|
|
39
41
|
override fun payServiceStatus(): PayServiceStatus {
|
|
40
42
|
return try {
|
|
41
|
-
val request = IsReadyToPayRequest.fromJson(
|
|
42
|
-
GooglePayRequestBuilder.createIsReadyToPayRequest().toString()
|
|
43
|
-
)
|
|
44
|
-
|
|
45
43
|
val client = createPaymentsClient(currentEnvironment)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
val canSetupCards = isReadyToPay(
|
|
45
|
+
client = client,
|
|
46
|
+
requestJson = GooglePayRequestBuilder.createIsReadyToPayRequest(
|
|
47
|
+
existingPaymentMethodRequired = false
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
val canMakePayments = isReadyToPay(
|
|
51
|
+
client = client,
|
|
52
|
+
requestJson = GooglePayRequestBuilder.createIsReadyToPayRequest(
|
|
53
|
+
existingPaymentMethodRequired = true
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
PayServiceStatus(
|
|
57
|
+
canMakePayments = canMakePayments,
|
|
58
|
+
canSetupCards = canSetupCards
|
|
59
|
+
)
|
|
50
60
|
} catch (e: Exception) {
|
|
51
61
|
Log.e(PaymentConstants.TAG_PAYMENT_HANDLER, "Error checking status", e)
|
|
52
62
|
PayServiceStatus(canMakePayments = false, canSetupCards = false)
|
|
@@ -143,6 +153,15 @@ class HybridPaymentHandler : HybridPaymentHandlerSpec(), ActivityEventListener {
|
|
|
143
153
|
GooglePayEnvironment.TEST, null -> WalletConstants.ENVIRONMENT_TEST
|
|
144
154
|
}
|
|
145
155
|
}
|
|
156
|
+
|
|
157
|
+
private fun isReadyToPay(
|
|
158
|
+
client: PaymentsClient,
|
|
159
|
+
requestJson: org.json.JSONObject
|
|
160
|
+
): Boolean {
|
|
161
|
+
val request = IsReadyToPayRequest.fromJson(requestJson.toString())
|
|
162
|
+
val task = client.isReadyToPay(request)
|
|
163
|
+
return Tasks.await(task, 5, TimeUnit.SECONDS) ?: false
|
|
164
|
+
}
|
|
146
165
|
|
|
147
166
|
private fun launchPaymentUI(
|
|
148
167
|
paymentDataRequest: org.json.JSONObject,
|
|
@@ -6,21 +6,31 @@ import NitroModules
|
|
|
6
6
|
|
|
7
7
|
private enum ErrorMessage {
|
|
8
8
|
static let paymentCancelled = "Payment cancelled by user"
|
|
9
|
+
static let paymentDismissed = "Payment sheet was dismissed before authorization"
|
|
10
|
+
static let missingMerchantIdentifier = "No Apple Pay merchant identifier configured"
|
|
9
11
|
static let unableToPresent = "Unable to present payment authorization"
|
|
10
12
|
static let unableToCreate = "Unable to create payment authorization"
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
private enum BundleKey {
|
|
16
|
+
static let applePayMerchantIdentifiers = "ReactNativePayApplePayMerchantIdentifiers"
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
// MARK: - Payment Request Builder
|
|
14
20
|
|
|
15
21
|
private struct PaymentRequestBuilder {
|
|
16
22
|
|
|
17
|
-
static func build(from request: PaymentRequest) -> PKPaymentRequest {
|
|
23
|
+
static func build(from request: PaymentRequest) -> PKPaymentRequest? {
|
|
24
|
+
guard let merchantIdentifier = resolveMerchantIdentifier(from: request) else {
|
|
25
|
+
return nil
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
let paymentRequest = PKPaymentRequest()
|
|
19
29
|
|
|
20
|
-
paymentRequest.merchantIdentifier =
|
|
30
|
+
paymentRequest.merchantIdentifier = merchantIdentifier
|
|
21
31
|
paymentRequest.countryCode = request.countryCode
|
|
22
32
|
paymentRequest.currencyCode = request.currencyCode
|
|
23
|
-
paymentRequest.paymentSummaryItems = buildPaymentItems(request
|
|
33
|
+
paymentRequest.paymentSummaryItems = buildPaymentItems(for: request)
|
|
24
34
|
paymentRequest.merchantCapabilities = buildMerchantCapabilities(request.merchantCapabilities)
|
|
25
35
|
paymentRequest.supportedNetworks = buildSupportedNetworks(request.supportedNetworks)
|
|
26
36
|
|
|
@@ -32,9 +42,33 @@ private struct PaymentRequestBuilder {
|
|
|
32
42
|
|
|
33
43
|
return paymentRequest
|
|
34
44
|
}
|
|
45
|
+
|
|
46
|
+
private static func resolveMerchantIdentifier(from request: PaymentRequest) -> String? {
|
|
47
|
+
if let overrideIdentifier = request.applePayMerchantIdentifier?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines),
|
|
48
|
+
!overrideIdentifier.isEmpty {
|
|
49
|
+
return overrideIdentifier
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if let merchantIdentifiers = Bundle.main.object(
|
|
53
|
+
forInfoDictionaryKey: BundleKey.applePayMerchantIdentifiers
|
|
54
|
+
) as? [String] {
|
|
55
|
+
return merchantIdentifiers.first {
|
|
56
|
+
!$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if let merchantIdentifier = Bundle.main.object(
|
|
61
|
+
forInfoDictionaryKey: BundleKey.applePayMerchantIdentifiers
|
|
62
|
+
) as? String {
|
|
63
|
+
let trimmedIdentifier = merchantIdentifier.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
|
64
|
+
return trimmedIdentifier.isEmpty ? nil : trimmedIdentifier
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
35
69
|
|
|
36
|
-
private static func buildPaymentItems(
|
|
37
|
-
|
|
70
|
+
private static func buildPaymentItems(for request: PaymentRequest) -> [PKPaymentSummaryItem] {
|
|
71
|
+
let lineItems = request.paymentItems.map { item in
|
|
38
72
|
let pkItem = PKPaymentSummaryItem(
|
|
39
73
|
label: item.label,
|
|
40
74
|
amount: NSDecimalNumber(decimal: Decimal(item.amount))
|
|
@@ -42,6 +76,29 @@ private struct PaymentRequestBuilder {
|
|
|
42
76
|
pkItem.type = item.type == .final ? .final : .pending
|
|
43
77
|
return pkItem
|
|
44
78
|
}
|
|
79
|
+
|
|
80
|
+
guard request.paymentItems.count > 1 else {
|
|
81
|
+
return lineItems
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let totalAmount = request.paymentItems.reduce(Decimal.zero) { partialResult, item in
|
|
85
|
+
partialResult + Decimal(item.amount)
|
|
86
|
+
}
|
|
87
|
+
let totalLabel: String
|
|
88
|
+
if let merchantName = request.merchantName?
|
|
89
|
+
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines),
|
|
90
|
+
!merchantName.isEmpty {
|
|
91
|
+
totalLabel = merchantName
|
|
92
|
+
} else {
|
|
93
|
+
totalLabel = "Total"
|
|
94
|
+
}
|
|
95
|
+
let totalItem = PKPaymentSummaryItem(
|
|
96
|
+
label: totalLabel,
|
|
97
|
+
amount: NSDecimalNumber(decimal: totalAmount)
|
|
98
|
+
)
|
|
99
|
+
totalItem.type = .final
|
|
100
|
+
|
|
101
|
+
return lineItems + [totalItem]
|
|
45
102
|
}
|
|
46
103
|
|
|
47
104
|
private static func buildMerchantCapabilities(_ capabilities: [String]) -> PKMerchantCapability {
|
|
@@ -129,10 +186,16 @@ private struct PaymentTokenConverter {
|
|
|
129
186
|
private class PaymentDelegate: NSObject, PKPaymentAuthorizationViewControllerDelegate {
|
|
130
187
|
private weak var paymentHandler: HybridPaymentHandler?
|
|
131
188
|
private var paymentAuthorized: Bool = false
|
|
189
|
+
private var presentationDate: Date?
|
|
190
|
+
private let userDismissThreshold: TimeInterval = 0.75
|
|
132
191
|
|
|
133
192
|
init(paymentHandler: HybridPaymentHandler) {
|
|
134
193
|
self.paymentHandler = paymentHandler
|
|
135
194
|
}
|
|
195
|
+
|
|
196
|
+
func markPresented() {
|
|
197
|
+
presentationDate = Date()
|
|
198
|
+
}
|
|
136
199
|
|
|
137
200
|
func paymentAuthorizationViewController(
|
|
138
201
|
_ controller: PKPaymentAuthorizationViewController,
|
|
@@ -163,11 +226,19 @@ private class PaymentDelegate: NSObject, PKPaymentAuthorizationViewControllerDel
|
|
|
163
226
|
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
|
|
164
227
|
controller.dismiss(animated: true) {
|
|
165
228
|
if !self.paymentAuthorized {
|
|
229
|
+
let errorMessage: String
|
|
230
|
+
if let presentationDate = self.presentationDate,
|
|
231
|
+
Date().timeIntervalSince(presentationDate) >= self.userDismissThreshold {
|
|
232
|
+
errorMessage = ErrorMessage.paymentCancelled
|
|
233
|
+
} else {
|
|
234
|
+
errorMessage = ErrorMessage.paymentDismissed
|
|
235
|
+
}
|
|
236
|
+
|
|
166
237
|
let result = PaymentResult.init(
|
|
167
238
|
success: false,
|
|
168
239
|
transactionId: nil,
|
|
169
240
|
token: nil,
|
|
170
|
-
error:
|
|
241
|
+
error: errorMessage
|
|
171
242
|
)
|
|
172
243
|
self.paymentHandler?.handlePaymentResult(result)
|
|
173
244
|
}
|
|
@@ -216,7 +287,10 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
216
287
|
// MARK: - Private Methods
|
|
217
288
|
|
|
218
289
|
private func performPayment(request: PaymentRequest, completion: @escaping (PaymentResult) -> Void) {
|
|
219
|
-
let paymentRequest = PaymentRequestBuilder.build(from: request)
|
|
290
|
+
guard let paymentRequest = PaymentRequestBuilder.build(from: request) else {
|
|
291
|
+
completion(createErrorResult(ErrorMessage.missingMerchantIdentifier))
|
|
292
|
+
return
|
|
293
|
+
}
|
|
220
294
|
|
|
221
295
|
paymentCompletion = completion
|
|
222
296
|
currentPaymentRequest = paymentRequest
|
|
@@ -229,12 +303,14 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
229
303
|
|
|
230
304
|
paymentAuthVC.delegate = delegate
|
|
231
305
|
|
|
232
|
-
guard let rootViewController =
|
|
306
|
+
guard let rootViewController = getRootViewController() else {
|
|
233
307
|
completion(createErrorResult(ErrorMessage.unableToPresent))
|
|
234
308
|
return
|
|
235
309
|
}
|
|
236
310
|
|
|
237
|
-
rootViewController.present(paymentAuthVC, animated: true)
|
|
311
|
+
rootViewController.present(paymentAuthVC, animated: true) {
|
|
312
|
+
self.delegate?.markPresented()
|
|
313
|
+
}
|
|
238
314
|
}
|
|
239
315
|
|
|
240
316
|
private func createErrorResult(_ error: String) -> PaymentResult {
|
|
@@ -245,4 +321,35 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
245
321
|
error: error
|
|
246
322
|
)
|
|
247
323
|
}
|
|
324
|
+
|
|
325
|
+
private func getRootViewController() -> UIViewController? {
|
|
326
|
+
let scenes = UIApplication.shared.connectedScenes
|
|
327
|
+
.compactMap { $0 as? UIWindowScene }
|
|
328
|
+
.filter { $0.activationState == .foregroundActive }
|
|
329
|
+
|
|
330
|
+
let rootViewController = scenes
|
|
331
|
+
.flatMap(\.windows)
|
|
332
|
+
.first(where: \.isKeyWindow)?
|
|
333
|
+
.rootViewController
|
|
334
|
+
|
|
335
|
+
return topViewController(from: rootViewController)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private func topViewController(from viewController: UIViewController?) -> UIViewController? {
|
|
339
|
+
guard let viewController else { return nil }
|
|
340
|
+
|
|
341
|
+
if let navigationController = viewController as? UINavigationController {
|
|
342
|
+
return topViewController(from: navigationController.visibleViewController)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if let tabBarController = viewController as? UITabBarController {
|
|
346
|
+
return topViewController(from: tabBarController.selectedViewController)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if let presentedViewController = viewController.presentedViewController {
|
|
350
|
+
return topViewController(from: presentedViewController)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return viewController
|
|
354
|
+
}
|
|
248
355
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_native_1 = require("@testing-library/react-native");
|
|
4
|
+
const mockPayServiceStatus = jest.fn();
|
|
5
|
+
const mockStartPayment = jest.fn();
|
|
6
|
+
jest.mock('react-native-nitro-modules', () => ({
|
|
7
|
+
NitroModules: {
|
|
8
|
+
createHybridObject: jest.fn(() => ({
|
|
9
|
+
payServiceStatus: (...args) => mockPayServiceStatus(...args),
|
|
10
|
+
startPayment: (...args) => mockStartPayment(...args),
|
|
11
|
+
})),
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
const usePaymentCheckout_1 = require("../usePaymentCheckout");
|
|
15
|
+
describe('usePaymentCheckout integration', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockPayServiceStatus.mockReturnValue({
|
|
18
|
+
canMakePayments: true,
|
|
19
|
+
canSetupCards: true,
|
|
20
|
+
});
|
|
21
|
+
mockStartPayment.mockReset();
|
|
22
|
+
});
|
|
23
|
+
const renderCheckout = () => (0, react_native_1.renderHook)(() => (0, usePaymentCheckout_1.usePaymentCheckout)({}));
|
|
24
|
+
it('loads payment service status on mount', async () => {
|
|
25
|
+
const { result } = renderCheckout();
|
|
26
|
+
await (0, react_native_1.waitFor)(() => {
|
|
27
|
+
expect(result.current.isCheckingStatus).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
expect(result.current.canMakePayments).toBe(true);
|
|
30
|
+
expect(result.current.canSetupCards).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('falls back to unavailable status when status check fails', async () => {
|
|
33
|
+
mockPayServiceStatus.mockImplementationOnce(() => {
|
|
34
|
+
throw new Error('Native status failure');
|
|
35
|
+
});
|
|
36
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
37
|
+
const { result } = renderCheckout();
|
|
38
|
+
await (0, react_native_1.waitFor)(() => {
|
|
39
|
+
expect(result.current.isCheckingStatus).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
expect(result.current.canMakePayments).toBe(false);
|
|
42
|
+
expect(result.current.canSetupCards).toBe(false);
|
|
43
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
44
|
+
consoleSpy.mockRestore();
|
|
45
|
+
});
|
|
46
|
+
it('manages cart operations and total correctly', async () => {
|
|
47
|
+
const { result } = renderCheckout();
|
|
48
|
+
await (0, react_native_1.waitFor)(() => {
|
|
49
|
+
expect(result.current.isCheckingStatus).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
(0, react_native_1.act)(() => {
|
|
52
|
+
result.current.addItem('Coffee', 4.5);
|
|
53
|
+
result.current.addItems([
|
|
54
|
+
{ label: 'Sandwich', amount: 8.25 },
|
|
55
|
+
{ label: 'Tip', amount: 1.25, type: 'pending' },
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
expect(result.current.items).toEqual([
|
|
59
|
+
{ label: 'Coffee', amount: 4.5, type: 'final' },
|
|
60
|
+
{ label: 'Sandwich', amount: 8.25, type: 'final' },
|
|
61
|
+
{ label: 'Tip', amount: 1.25, type: 'pending' },
|
|
62
|
+
]);
|
|
63
|
+
expect(result.current.total).toBeCloseTo(14);
|
|
64
|
+
(0, react_native_1.act)(() => {
|
|
65
|
+
result.current.updateItem(0, { amount: 5 });
|
|
66
|
+
});
|
|
67
|
+
expect(result.current.items[0]?.amount).toBe(5);
|
|
68
|
+
expect(result.current.total).toBeCloseTo(14.5);
|
|
69
|
+
(0, react_native_1.act)(() => {
|
|
70
|
+
result.current.removeItem(1);
|
|
71
|
+
});
|
|
72
|
+
expect(result.current.items.map((item) => item.label)).toEqual([
|
|
73
|
+
'Coffee',
|
|
74
|
+
'Tip',
|
|
75
|
+
]);
|
|
76
|
+
(0, react_native_1.act)(() => {
|
|
77
|
+
result.current.clearItems();
|
|
78
|
+
});
|
|
79
|
+
expect(result.current.items).toEqual([]);
|
|
80
|
+
expect(result.current.total).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
it('provides default fallback payment item when cart is empty', () => {
|
|
83
|
+
const { result } = renderCheckout();
|
|
84
|
+
expect(result.current.paymentRequest.paymentItems).toEqual([
|
|
85
|
+
{ label: 'Total', amount: 0, type: 'final' },
|
|
86
|
+
]);
|
|
87
|
+
expect(result.current.paymentRequest.countryCode).toBe('US');
|
|
88
|
+
expect(result.current.paymentRequest.currencyCode).toBe('USD');
|
|
89
|
+
});
|
|
90
|
+
it('rejects startPayment when cart is empty', async () => {
|
|
91
|
+
const { result } = renderCheckout();
|
|
92
|
+
let paymentResult = 'not-called';
|
|
93
|
+
await (0, react_native_1.act)(async () => {
|
|
94
|
+
paymentResult = await result.current.startPayment();
|
|
95
|
+
});
|
|
96
|
+
expect(paymentResult).toBeNull();
|
|
97
|
+
expect(result.current.error?.message).toBe('Cart is empty');
|
|
98
|
+
expect(mockStartPayment).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
it('handles successful payments', async () => {
|
|
101
|
+
const nativeSuccess = { success: true, transactionId: 'tx_123' };
|
|
102
|
+
mockStartPayment.mockResolvedValueOnce(nativeSuccess);
|
|
103
|
+
const { result } = renderCheckout();
|
|
104
|
+
(0, react_native_1.act)(() => {
|
|
105
|
+
result.current.addItem('Coffee', 4.99);
|
|
106
|
+
});
|
|
107
|
+
let paymentResult = null;
|
|
108
|
+
await (0, react_native_1.act)(async () => {
|
|
109
|
+
paymentResult = await result.current.startPayment();
|
|
110
|
+
});
|
|
111
|
+
expect(mockStartPayment).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(mockStartPayment).toHaveBeenCalledWith(expect.objectContaining({
|
|
113
|
+
paymentItems: [{ label: 'Coffee', amount: 4.99, type: 'final' }],
|
|
114
|
+
}));
|
|
115
|
+
expect(paymentResult).toEqual(nativeSuccess);
|
|
116
|
+
expect(result.current.result).toEqual(nativeSuccess);
|
|
117
|
+
expect(result.current.error).toBeNull();
|
|
118
|
+
expect(result.current.isProcessing).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
it('sets an error when native payment response has success=false', async () => {
|
|
121
|
+
mockStartPayment.mockResolvedValueOnce({
|
|
122
|
+
success: false,
|
|
123
|
+
error: 'Declined by issuer',
|
|
124
|
+
});
|
|
125
|
+
const { result } = renderCheckout();
|
|
126
|
+
(0, react_native_1.act)(() => {
|
|
127
|
+
result.current.addItem('Order', 9.99);
|
|
128
|
+
});
|
|
129
|
+
await (0, react_native_1.act)(async () => {
|
|
130
|
+
await result.current.startPayment();
|
|
131
|
+
});
|
|
132
|
+
expect(result.current.error?.message).toBe('Declined by issuer');
|
|
133
|
+
expect(result.current.result).toEqual({
|
|
134
|
+
success: false,
|
|
135
|
+
error: 'Declined by issuer',
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
it('forwards Apple Pay override and Google Pay config when provided', async () => {
|
|
139
|
+
mockStartPayment.mockResolvedValueOnce({ success: true });
|
|
140
|
+
const { result } = (0, react_native_1.renderHook)(() => (0, usePaymentCheckout_1.usePaymentCheckout)({
|
|
141
|
+
applePayMerchantIdentifier: 'merchant.com.apple.override',
|
|
142
|
+
googlePayMerchantId: 'google-pay-merchant-id',
|
|
143
|
+
googlePayEnvironment: 'PRODUCTION',
|
|
144
|
+
googlePayGateway: 'stripe',
|
|
145
|
+
googlePayGatewayMerchantId: 'gateway-merchant-id',
|
|
146
|
+
}));
|
|
147
|
+
(0, react_native_1.act)(() => {
|
|
148
|
+
result.current.addItem('Order', 12);
|
|
149
|
+
});
|
|
150
|
+
await (0, react_native_1.act)(async () => {
|
|
151
|
+
await result.current.startPayment();
|
|
152
|
+
});
|
|
153
|
+
expect(mockStartPayment).toHaveBeenCalledWith(expect.objectContaining({
|
|
154
|
+
applePayMerchantIdentifier: 'merchant.com.apple.override',
|
|
155
|
+
googlePayMerchantId: 'google-pay-merchant-id',
|
|
156
|
+
googlePayEnvironment: 'PRODUCTION',
|
|
157
|
+
googlePayGateway: 'stripe',
|
|
158
|
+
googlePayGatewayMerchantId: 'gateway-merchant-id',
|
|
159
|
+
}));
|
|
160
|
+
});
|
|
161
|
+
it('uses generic error for non-Error thrown values', async () => {
|
|
162
|
+
mockStartPayment.mockRejectedValueOnce('native crash');
|
|
163
|
+
const { result } = renderCheckout();
|
|
164
|
+
(0, react_native_1.act)(() => {
|
|
165
|
+
result.current.addItem('Order', 12);
|
|
166
|
+
});
|
|
167
|
+
let paymentResult = 'not-null';
|
|
168
|
+
await (0, react_native_1.act)(async () => {
|
|
169
|
+
paymentResult = await result.current.startPayment();
|
|
170
|
+
});
|
|
171
|
+
expect(paymentResult).toBeNull();
|
|
172
|
+
expect(result.current.error?.message).toBe('Payment processing failed');
|
|
173
|
+
});
|
|
174
|
+
it('resets transient checkout state', async () => {
|
|
175
|
+
mockStartPayment.mockResolvedValueOnce({ success: false, error: 'Failed' });
|
|
176
|
+
const { result } = renderCheckout();
|
|
177
|
+
(0, react_native_1.act)(() => {
|
|
178
|
+
result.current.addItem('Order', 3);
|
|
179
|
+
});
|
|
180
|
+
await (0, react_native_1.act)(async () => {
|
|
181
|
+
await result.current.startPayment();
|
|
182
|
+
});
|
|
183
|
+
expect(result.current.error).not.toBeNull();
|
|
184
|
+
(0, react_native_1.act)(() => {
|
|
185
|
+
result.current.reset();
|
|
186
|
+
});
|
|
187
|
+
expect(result.current.error).toBeNull();
|
|
188
|
+
expect(result.current.result).toBeNull();
|
|
189
|
+
expect(result.current.isProcessing).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -1,13 +1,56 @@
|
|
|
1
1
|
import type { PaymentRequest, PaymentResult, PaymentItem, GooglePayEnvironment } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for `usePaymentCheckout`.
|
|
4
|
+
*/
|
|
2
5
|
export interface UsePaymentCheckoutConfig {
|
|
3
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Merchant name shown by payment providers that support displaying it.
|
|
8
|
+
* Used primarily by Google Pay.
|
|
9
|
+
*/
|
|
4
10
|
merchantName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* ISO 3166-1 alpha-2 country code for the transaction.
|
|
13
|
+
* Defaults to `'US'`.
|
|
14
|
+
*/
|
|
5
15
|
countryCode?: string;
|
|
16
|
+
/**
|
|
17
|
+
* ISO 4217 currency code for all payment items.
|
|
18
|
+
* Defaults to `'USD'`.
|
|
19
|
+
*/
|
|
6
20
|
currencyCode?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Supported card networks for the payment sheet.
|
|
23
|
+
* Defaults to `['visa', 'mastercard', 'amex', 'discover']`.
|
|
24
|
+
*/
|
|
7
25
|
supportedNetworks?: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Merchant capabilities passed to Apple Pay.
|
|
28
|
+
* Defaults to `['3DS']`.
|
|
29
|
+
*/
|
|
8
30
|
merchantCapabilities?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* Optional Apple Pay Merchant ID override.
|
|
33
|
+
* When omitted, iOS reads the Merchant ID from the app entitlements.
|
|
34
|
+
*/
|
|
35
|
+
applePayMerchantIdentifier?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Google Pay merchant ID used in Android production requests.
|
|
38
|
+
* Not needed for iOS.
|
|
39
|
+
*/
|
|
40
|
+
googlePayMerchantId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Google Pay environment for Android requests.
|
|
43
|
+
* Use `'TEST'` for sandbox flows and `'PRODUCTION'` for live payments.
|
|
44
|
+
*/
|
|
9
45
|
googlePayEnvironment?: GooglePayEnvironment;
|
|
46
|
+
/**
|
|
47
|
+
* Payment gateway identifier for Google Pay tokenization.
|
|
48
|
+
* Examples include `'stripe'`, `'braintree'`, and `'adyen'`.
|
|
49
|
+
*/
|
|
10
50
|
googlePayGateway?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Merchant ID provided by your payment gateway for Google Pay tokenization.
|
|
53
|
+
*/
|
|
11
54
|
googlePayGatewayMerchantId?: string;
|
|
12
55
|
}
|
|
13
56
|
export interface UsePaymentCheckoutReturn {
|
|
@@ -41,7 +84,8 @@ export interface UsePaymentCheckoutReturn {
|
|
|
41
84
|
* - Payment processing
|
|
42
85
|
* - State management
|
|
43
86
|
*
|
|
44
|
-
* @param config -
|
|
87
|
+
* @param config - Hook configuration including locale, supported networks,
|
|
88
|
+
* Apple Pay override, and Google Pay gateway settings.
|
|
45
89
|
* @returns Complete payment checkout interface
|
|
46
90
|
*
|
|
47
91
|
* @example
|
|
@@ -58,9 +102,9 @@ export interface UsePaymentCheckoutReturn {
|
|
|
58
102
|
* isProcessing,
|
|
59
103
|
* error,
|
|
60
104
|
* } = usePaymentCheckout({
|
|
61
|
-
* merchantIdentifier: 'merchant.com.example',
|
|
62
105
|
* currencyCode: 'USD',
|
|
63
106
|
* countryCode: 'US',
|
|
107
|
+
* googlePayMerchantId: 'your_google_pay_merchant_id',
|
|
64
108
|
* })
|
|
65
109
|
*
|
|
66
110
|
* // Add single item
|