@gmisoftware/react-native-pay 0.0.12 → 0.0.13
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 +37 -10
- package/android/src/main/java/com/margelo/nitro/pay/HybridPaymentHandler.kt +27 -8
- package/ios/HybridPaymentHandler.swift +90 -6
- 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 +11 -4
- package/src/hooks/__tests__/usePaymentCheckout.integration.test.ts +244 -0
- package/src/hooks/usePaymentCheckout.ts +60 -5
- 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 +93 -0
- package/src/utils/paymentHelpers.ts +1 -6
|
@@ -12,13 +12,16 @@ object GooglePayRequestBuilder {
|
|
|
12
12
|
/**
|
|
13
13
|
* Creates an IsReadyToPay request
|
|
14
14
|
*/
|
|
15
|
-
fun createIsReadyToPayRequest(): JSONObject {
|
|
15
|
+
fun createIsReadyToPayRequest(existingPaymentMethodRequired: Boolean? = null): JSONObject {
|
|
16
16
|
return JSONObject().apply {
|
|
17
17
|
put("apiVersion", PaymentConstants.API_VERSION)
|
|
18
18
|
put("apiVersionMinor", PaymentConstants.API_VERSION_MINOR)
|
|
19
19
|
put("allowedPaymentMethods", JSONArray().apply {
|
|
20
20
|
put(createBaseCardPaymentMethod())
|
|
21
21
|
})
|
|
22
|
+
existingPaymentMethodRequired?.let {
|
|
23
|
+
put("existingPaymentMethodRequired", it)
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -29,6 +32,8 @@ object GooglePayRequestBuilder {
|
|
|
29
32
|
request: PaymentRequest,
|
|
30
33
|
environment: Int
|
|
31
34
|
): JSONObject {
|
|
35
|
+
validatePaymentRequest(request, environment)
|
|
36
|
+
|
|
32
37
|
return JSONObject().apply {
|
|
33
38
|
put("apiVersion", PaymentConstants.API_VERSION)
|
|
34
39
|
put("apiVersionMinor", PaymentConstants.API_VERSION_MINOR)
|
|
@@ -51,7 +56,7 @@ object GooglePayRequestBuilder {
|
|
|
51
56
|
put("merchantName", request.merchantName ?: PaymentConstants.DEFAULT_MERCHANT_NAME)
|
|
52
57
|
// Add merchant ID only for PRODUCTION environment
|
|
53
58
|
if (environment == WalletConstants.ENVIRONMENT_PRODUCTION) {
|
|
54
|
-
put("merchantId", request.
|
|
59
|
+
put("merchantId", request.googlePayMerchantId)
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
}
|
|
@@ -88,20 +93,42 @@ object GooglePayRequestBuilder {
|
|
|
88
93
|
request: PaymentRequest,
|
|
89
94
|
environment: Int
|
|
90
95
|
): JSONObject {
|
|
96
|
+
val isProduction = environment == WalletConstants.ENVIRONMENT_PRODUCTION
|
|
97
|
+
val gateway = if (isProduction) {
|
|
98
|
+
request.googlePayGateway!!.trim()
|
|
99
|
+
} else {
|
|
100
|
+
request.googlePayGateway?.trim().takeUnless { it.isNullOrEmpty() }
|
|
101
|
+
?: PaymentConstants.DEFAULT_GATEWAY
|
|
102
|
+
}
|
|
103
|
+
val gatewayMerchantId = if (isProduction) {
|
|
104
|
+
request.googlePayGatewayMerchantId!!.trim()
|
|
105
|
+
} else {
|
|
106
|
+
request.googlePayGatewayMerchantId?.trim().takeUnless { it.isNullOrEmpty() }
|
|
107
|
+
?: PaymentConstants.DEFAULT_GATEWAY_MERCHANT_ID
|
|
108
|
+
}
|
|
109
|
+
|
|
91
110
|
return JSONObject().apply {
|
|
92
111
|
put("type", PaymentConstants.TOKENIZATION_PAYMENT_GATEWAY)
|
|
93
112
|
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
|
-
)
|
|
113
|
+
put("gateway", gateway)
|
|
114
|
+
put("gatewayMerchantId", gatewayMerchantId)
|
|
102
115
|
})
|
|
103
116
|
}
|
|
104
117
|
}
|
|
118
|
+
|
|
119
|
+
private fun validatePaymentRequest(request: PaymentRequest, environment: Int) {
|
|
120
|
+
if (environment != WalletConstants.ENVIRONMENT_PRODUCTION) return
|
|
121
|
+
|
|
122
|
+
require(!request.googlePayMerchantId.isNullOrBlank()) {
|
|
123
|
+
"googlePayMerchantId is required in PRODUCTION"
|
|
124
|
+
}
|
|
125
|
+
require(!request.googlePayGateway.isNullOrBlank()) {
|
|
126
|
+
"googlePayGateway is required in PRODUCTION"
|
|
127
|
+
}
|
|
128
|
+
require(!request.googlePayGatewayMerchantId.isNullOrBlank()) {
|
|
129
|
+
"googlePayGatewayMerchantId is required in PRODUCTION"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
105
132
|
|
|
106
133
|
/**
|
|
107
134
|
* Creates transaction info
|
|
@@ -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,18 +6,28 @@ 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
33
|
paymentRequest.paymentSummaryItems = buildPaymentItems(request.paymentItems)
|
|
@@ -32,6 +42,30 @@ 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
70
|
private static func buildPaymentItems(_ items: [PaymentItem]) -> [PKPaymentSummaryItem] {
|
|
37
71
|
return items.map { item in
|
|
@@ -129,10 +163,16 @@ private struct PaymentTokenConverter {
|
|
|
129
163
|
private class PaymentDelegate: NSObject, PKPaymentAuthorizationViewControllerDelegate {
|
|
130
164
|
private weak var paymentHandler: HybridPaymentHandler?
|
|
131
165
|
private var paymentAuthorized: Bool = false
|
|
166
|
+
private var presentationDate: Date?
|
|
167
|
+
private let userDismissThreshold: TimeInterval = 0.75
|
|
132
168
|
|
|
133
169
|
init(paymentHandler: HybridPaymentHandler) {
|
|
134
170
|
self.paymentHandler = paymentHandler
|
|
135
171
|
}
|
|
172
|
+
|
|
173
|
+
func markPresented() {
|
|
174
|
+
presentationDate = Date()
|
|
175
|
+
}
|
|
136
176
|
|
|
137
177
|
func paymentAuthorizationViewController(
|
|
138
178
|
_ controller: PKPaymentAuthorizationViewController,
|
|
@@ -163,11 +203,19 @@ private class PaymentDelegate: NSObject, PKPaymentAuthorizationViewControllerDel
|
|
|
163
203
|
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
|
|
164
204
|
controller.dismiss(animated: true) {
|
|
165
205
|
if !self.paymentAuthorized {
|
|
206
|
+
let errorMessage: String
|
|
207
|
+
if let presentationDate = self.presentationDate,
|
|
208
|
+
Date().timeIntervalSince(presentationDate) >= self.userDismissThreshold {
|
|
209
|
+
errorMessage = ErrorMessage.paymentCancelled
|
|
210
|
+
} else {
|
|
211
|
+
errorMessage = ErrorMessage.paymentDismissed
|
|
212
|
+
}
|
|
213
|
+
|
|
166
214
|
let result = PaymentResult.init(
|
|
167
215
|
success: false,
|
|
168
216
|
transactionId: nil,
|
|
169
217
|
token: nil,
|
|
170
|
-
error:
|
|
218
|
+
error: errorMessage
|
|
171
219
|
)
|
|
172
220
|
self.paymentHandler?.handlePaymentResult(result)
|
|
173
221
|
}
|
|
@@ -216,7 +264,10 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
216
264
|
// MARK: - Private Methods
|
|
217
265
|
|
|
218
266
|
private func performPayment(request: PaymentRequest, completion: @escaping (PaymentResult) -> Void) {
|
|
219
|
-
let paymentRequest = PaymentRequestBuilder.build(from: request)
|
|
267
|
+
guard let paymentRequest = PaymentRequestBuilder.build(from: request) else {
|
|
268
|
+
completion(createErrorResult(ErrorMessage.missingMerchantIdentifier))
|
|
269
|
+
return
|
|
270
|
+
}
|
|
220
271
|
|
|
221
272
|
paymentCompletion = completion
|
|
222
273
|
currentPaymentRequest = paymentRequest
|
|
@@ -229,12 +280,14 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
229
280
|
|
|
230
281
|
paymentAuthVC.delegate = delegate
|
|
231
282
|
|
|
232
|
-
guard let rootViewController =
|
|
283
|
+
guard let rootViewController = getRootViewController() else {
|
|
233
284
|
completion(createErrorResult(ErrorMessage.unableToPresent))
|
|
234
285
|
return
|
|
235
286
|
}
|
|
236
287
|
|
|
237
|
-
rootViewController.present(paymentAuthVC, animated: true)
|
|
288
|
+
rootViewController.present(paymentAuthVC, animated: true) {
|
|
289
|
+
self.delegate?.markPresented()
|
|
290
|
+
}
|
|
238
291
|
}
|
|
239
292
|
|
|
240
293
|
private func createErrorResult(_ error: String) -> PaymentResult {
|
|
@@ -245,4 +298,35 @@ class HybridPaymentHandler: HybridPaymentHandlerSpec {
|
|
|
245
298
|
error: error
|
|
246
299
|
)
|
|
247
300
|
}
|
|
301
|
+
|
|
302
|
+
private func getRootViewController() -> UIViewController? {
|
|
303
|
+
let scenes = UIApplication.shared.connectedScenes
|
|
304
|
+
.compactMap { $0 as? UIWindowScene }
|
|
305
|
+
.filter { $0.activationState == .foregroundActive }
|
|
306
|
+
|
|
307
|
+
let rootViewController = scenes
|
|
308
|
+
.flatMap(\.windows)
|
|
309
|
+
.first(where: \.isKeyWindow)?
|
|
310
|
+
.rootViewController
|
|
311
|
+
|
|
312
|
+
return topViewController(from: rootViewController)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private func topViewController(from viewController: UIViewController?) -> UIViewController? {
|
|
316
|
+
guard let viewController else { return nil }
|
|
317
|
+
|
|
318
|
+
if let navigationController = viewController as? UINavigationController {
|
|
319
|
+
return topViewController(from: navigationController.visibleViewController)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if let tabBarController = viewController as? UITabBarController {
|
|
323
|
+
return topViewController(from: tabBarController.selectedViewController)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if let presentedViewController = viewController.presentedViewController {
|
|
327
|
+
return topViewController(from: presentedViewController)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return viewController
|
|
331
|
+
}
|
|
248
332
|
}
|
|
@@ -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
|
|
@@ -14,7 +14,8 @@ const utils_1 = require("../utils");
|
|
|
14
14
|
* - Payment processing
|
|
15
15
|
* - State management
|
|
16
16
|
*
|
|
17
|
-
* @param config -
|
|
17
|
+
* @param config - Hook configuration including locale, supported networks,
|
|
18
|
+
* Apple Pay override, and Google Pay gateway settings.
|
|
18
19
|
* @returns Complete payment checkout interface
|
|
19
20
|
*
|
|
20
21
|
* @example
|
|
@@ -31,9 +32,9 @@ const utils_1 = require("../utils");
|
|
|
31
32
|
* isProcessing,
|
|
32
33
|
* error,
|
|
33
34
|
* } = usePaymentCheckout({
|
|
34
|
-
* merchantIdentifier: 'merchant.com.example',
|
|
35
35
|
* currencyCode: 'USD',
|
|
36
36
|
* countryCode: 'US',
|
|
37
|
+
* googlePayMerchantId: 'your_google_pay_merchant_id',
|
|
37
38
|
* })
|
|
38
39
|
*
|
|
39
40
|
* // Add single item
|
|
@@ -88,15 +89,16 @@ function usePaymentCheckout(config) {
|
|
|
88
89
|
}, []);
|
|
89
90
|
const total = (0, react_1.useMemo)(() => (0, utils_1.calculateTotal)(items), [items]);
|
|
90
91
|
const paymentRequest = (0, react_1.useMemo)(() => {
|
|
91
|
-
const {
|
|
92
|
+
const { merchantName, countryCode = 'US', currencyCode = 'USD', supportedNetworks = ['visa', 'mastercard', 'amex', 'discover'], merchantCapabilities = ['3DS'], applePayMerchantIdentifier, googlePayMerchantId, googlePayEnvironment, googlePayGateway, googlePayGatewayMerchantId, } = config;
|
|
92
93
|
return {
|
|
93
|
-
|
|
94
|
+
applePayMerchantIdentifier,
|
|
94
95
|
countryCode,
|
|
95
96
|
merchantName,
|
|
96
97
|
currencyCode,
|
|
97
98
|
supportedNetworks,
|
|
98
99
|
merchantCapabilities,
|
|
99
100
|
paymentItems: items.length > 0 ? items : [(0, utils_1.createPaymentItem)('Total', 0, 'final')],
|
|
101
|
+
googlePayMerchantId,
|
|
100
102
|
googlePayEnvironment,
|
|
101
103
|
googlePayGateway,
|
|
102
104
|
googlePayGatewayMerchantId,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const mockWithGooglePay = jest.fn((config, _props) => ({
|
|
7
|
+
...config,
|
|
8
|
+
googlePay: true,
|
|
9
|
+
}));
|
|
10
|
+
const mockWithApplePay = jest.fn((config, _props) => ({
|
|
11
|
+
...config,
|
|
12
|
+
applePay: true,
|
|
13
|
+
}));
|
|
14
|
+
jest.mock('../withGooglePay', () => ({
|
|
15
|
+
withGooglePay: (config, props) => mockWithGooglePay(config, props),
|
|
16
|
+
}));
|
|
17
|
+
jest.mock('../withApplePay', () => ({
|
|
18
|
+
withApplePay: (config, props) => mockWithApplePay(config, props),
|
|
19
|
+
}));
|
|
20
|
+
const index_1 = __importDefault(require("../index"));
|
|
21
|
+
describe('withReactNativePay', () => {
|
|
22
|
+
it('applies Google Pay and Apple Pay plugins in order', () => {
|
|
23
|
+
const inputConfig = { name: 'my-app' };
|
|
24
|
+
const props = {
|
|
25
|
+
merchantIdentifier: 'merchant.com.test',
|
|
26
|
+
enableGooglePay: true,
|
|
27
|
+
};
|
|
28
|
+
const result = (0, index_1.default)(inputConfig, props);
|
|
29
|
+
expect(mockWithGooglePay).toHaveBeenCalledWith(inputConfig, props);
|
|
30
|
+
expect(mockWithApplePay).toHaveBeenCalledWith({ name: 'my-app', googlePay: true }, props);
|
|
31
|
+
expect(result).toEqual({ name: 'my-app', googlePay: true, applePay: true });
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|