pluspay-a2a-react-native 0.1.0

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.
Files changed (47) hide show
  1. package/LICENSE +20 -0
  2. package/PluspayA2aReactNative.podspec +20 -0
  3. package/README.md +349 -0
  4. package/android/build.gradle +67 -0
  5. package/android/src/main/AndroidManifest.xml +11 -0
  6. package/android/src/main/java/com/pluspaya2areactnative/PluspayA2aReactNativeModule.kt +115 -0
  7. package/android/src/main/java/com/pluspaya2areactnative/PluspayA2aReactNativePackage.kt +31 -0
  8. package/ios/PluspayA2aReactNative.h +5 -0
  9. package/ios/PluspayA2aReactNative.mm +37 -0
  10. package/lib/module/NativePluspayA2aReactNative.js +14 -0
  11. package/lib/module/NativePluspayA2aReactNative.js.map +1 -0
  12. package/lib/module/client.js +112 -0
  13. package/lib/module/client.js.map +1 -0
  14. package/lib/module/index.js +8 -0
  15. package/lib/module/index.js.map +1 -0
  16. package/lib/module/models/enums.js +190 -0
  17. package/lib/module/models/enums.js.map +1 -0
  18. package/lib/module/models/exceptions.js +19 -0
  19. package/lib/module/models/exceptions.js.map +1 -0
  20. package/lib/module/models/requests.js +197 -0
  21. package/lib/module/models/requests.js.map +1 -0
  22. package/lib/module/models/responses.js +2 -0
  23. package/lib/module/models/responses.js.map +1 -0
  24. package/lib/module/package.json +1 -0
  25. package/lib/typescript/package.json +1 -0
  26. package/lib/typescript/src/NativePluspayA2aReactNative.d.ts +25 -0
  27. package/lib/typescript/src/NativePluspayA2aReactNative.d.ts.map +1 -0
  28. package/lib/typescript/src/client.d.ts +50 -0
  29. package/lib/typescript/src/client.d.ts.map +1 -0
  30. package/lib/typescript/src/index.d.ts +6 -0
  31. package/lib/typescript/src/index.d.ts.map +1 -0
  32. package/lib/typescript/src/models/enums.d.ts +169 -0
  33. package/lib/typescript/src/models/enums.d.ts.map +1 -0
  34. package/lib/typescript/src/models/exceptions.d.ts +10 -0
  35. package/lib/typescript/src/models/exceptions.d.ts.map +1 -0
  36. package/lib/typescript/src/models/requests.d.ts +128 -0
  37. package/lib/typescript/src/models/requests.d.ts.map +1 -0
  38. package/lib/typescript/src/models/responses.d.ts +51 -0
  39. package/lib/typescript/src/models/responses.d.ts.map +1 -0
  40. package/package.json +127 -0
  41. package/src/NativePluspayA2aReactNative.ts +27 -0
  42. package/src/client.ts +137 -0
  43. package/src/index.tsx +5 -0
  44. package/src/models/enums.ts +186 -0
  45. package/src/models/exceptions.ts +19 -0
  46. package/src/models/requests.ts +302 -0
  47. package/src/models/responses.ts +56 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fatih Can Yazici
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "PluspayA2aReactNative"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/plus-pay-tr/pluspay_a2a_react_native.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ install_modules_dependencies(s)
20
+ end
package/README.md ADDED
@@ -0,0 +1,349 @@
1
+ # pluspay-a2a-react-native
2
+
3
+ **POS+ (Pluspay)** Android uygulaması ile App-to-App (A2A) iletişim kurmak için geliştirilmiş React Native paketi. Bu paket, React Native uygulamanızdan POS+ uygulamasını başlatarak ödeme, iptal, EFT işlemleri, sipariş ödemeleri, gün sonu raporları ve parametre güncellemeleri yapmanızı sağlar.
4
+
5
+ > **Not:** Bu paket yalnızca Android platformunu desteklemektedir. iOS'ta tüm çağrılar `UNSUPPORTED_PLATFORM` ile reddedilir.
6
+
7
+ ## Kurulum
8
+
9
+ Paketi npm üzerinden ekleyin:
10
+
11
+ ```sh
12
+ yarn add pluspay-a2a-react-native
13
+ # veya npm:
14
+ npm install pluspay-a2a-react-native
15
+ ```
16
+
17
+ RN ≥ 0.60 **autolinking** native modülü otomatik bağlar; ek kurulum gerekmez. Android 11+ paket görünürlüğü için gerekli `<queries>` girdisi paket manifestinde tanımlıdır ve host uygulamaya merge olur.
18
+
19
+ ## Hızlı Başlangıç
20
+
21
+ ```ts
22
+ import {
23
+ PPA2AClient,
24
+ PPA2AException,
25
+ PPPaymentType,
26
+ PPPaymentMethod,
27
+ PPStartPaymentRequestModel,
28
+ } from 'pluspay-a2a-react-native';
29
+
30
+ // 1. İstemciyi oluşturun ve başlatın
31
+ const pluspay = new PPA2AClient();
32
+ await pluspay.initialize();
33
+
34
+ // 2. Ödeme başlatın
35
+ try {
36
+ const result = await pluspay.startPayment(
37
+ PPStartPaymentRequestModel.toRequest({
38
+ orderCode: 'ORD-001',
39
+ totalAmount: 150.0,
40
+ paymentType: PPPaymentType.POS,
41
+ paymentMethod: PPPaymentMethod.CC,
42
+ })
43
+ );
44
+ console.log('Ödeme başarılı:', result.status);
45
+ } catch (e) {
46
+ if (e instanceof PPA2AException) {
47
+ console.log('Ödeme başarısız:', e.errorCode, e.message);
48
+ }
49
+ }
50
+
51
+ // 3. İşiniz bittiğinde kaynakları temizleyin
52
+ await pluspay.dispose();
53
+ ```
54
+
55
+ ## İstemci Metodları
56
+
57
+ Tüm metodlar `PPA2AClient` sınıfı üzerindedir. Her metod başarılı durumda bir yanıt nesnesi (POS+'tan gelen snake_case JSON) döner, hata durumunda `PPA2AException` fırlatır.
58
+
59
+ | Metod | İstek Builder'ı | Açıklama |
60
+ |-------|-----------------|----------|
61
+ | `startPayment` | `PPStartPaymentRequestModel.toRequest` | Ödeme başlat |
62
+ | `cancelPayment` | `PPCancelPaymentRequestModel.toRequest` | Ödeme iptal et |
63
+ | `startEftPayment` | `PPEftPaymentRequestModel.toRequest` | EFT POS ödemesi başlat |
64
+ | `cancelEftPayment` | `PPEftCancelRequestModel.toRequest` | EFT POS ödemesini iptal et |
65
+ | `startOrderPayment` | `PPOrderPaymentRequestModel.toRequest` | Sipariş ödemesi başlat |
66
+ | `startMultiPayment` | `PPMultiPaymentRequest.toRequest` | Çoklu ödeme başlat |
67
+ | `triggerEod` | `PPEodRequestModel.toRequest` | Gün sonu tetikle |
68
+ | `triggerParameters` | `PPParameterRequestModel.toRequest` | Parametre güncellemesi tetikle |
69
+
70
+ ## İstek Modelleri
71
+
72
+ Her istek builder'ı, düz ve kullanışlı bir API sunan **`toRequest`** fonksiyonuna sahiptir. İlgili alanları doğrudan geçirirsiniz; builder dahili `header` + `data` yapısını sizin için oluşturur.
73
+
74
+ ### Ödeme Başlat
75
+
76
+ ```ts
77
+ PPStartPaymentRequestModel.toRequest({
78
+ orderCode: 'ORD-001',
79
+ totalAmount: 100.0,
80
+ paymentType: PPPaymentType.POS,
81
+ paymentMethod: PPPaymentMethod.CC,
82
+ installment: 3, // opsiyonel
83
+ isPartial: false, // opsiyonel, varsayılan: false
84
+ partialType: undefined, // opsiyonel
85
+ products: [], // opsiyonel
86
+ billingInformation: undefined, // opsiyonel
87
+ });
88
+ ```
89
+
90
+ ### Ödeme İptal
91
+
92
+ ```ts
93
+ PPCancelPaymentRequestModel.toRequest({
94
+ orderCode: 'ORD-001',
95
+ transactionId: 'TX-123',
96
+ note: 'Müşteri iptali talep etti', // opsiyonel
97
+ });
98
+ ```
99
+
100
+ ### EFT Ödeme
101
+
102
+ ```ts
103
+ PPEftPaymentRequestModel.toRequest({
104
+ totalAmount: 250.0,
105
+ paymentType: PPPaymentType.POS,
106
+ paymentMethod: PPPaymentMethod.CC,
107
+ transactionId: 'EFT-001',
108
+ taxRate: 18,
109
+ installment: undefined, // opsiyonel
110
+ });
111
+ ```
112
+
113
+ ### EFT İptal
114
+
115
+ ```ts
116
+ PPEftCancelRequestModel.toRequest({
117
+ transactionId: 'EFT-001',
118
+ totalAmount: 250.0,
119
+ });
120
+ ```
121
+
122
+ ### Sipariş Ödemesi
123
+
124
+ ```ts
125
+ PPOrderPaymentRequestModel.toRequest({
126
+ orderCode: 'ORD-001',
127
+ });
128
+ ```
129
+
130
+ ### Gün Sonu (EOD)
131
+
132
+ ```ts
133
+ PPEodRequestModel.toRequest({
134
+ isAll: false,
135
+ types: [PPEodType.POS, PPEodType.MULTINET],
136
+ });
137
+ ```
138
+
139
+ ### Parametre Yükleme
140
+
141
+ ```ts
142
+ PPParameterRequestModel.toRequest({
143
+ isAll: true,
144
+ types: [PPParameterTypes.bank, PPParameterTypes.multinet],
145
+ });
146
+ ```
147
+
148
+ ### Çoklu Ödeme (Multi Payment)
149
+
150
+ ```ts
151
+ PPMultiPaymentRequest.toRequest({
152
+ orderCode: 'ORD-001',
153
+ orderDate: new Date(),
154
+ changePaymentStatus: true,
155
+ products: [
156
+ {
157
+ id: 1,
158
+ sku: 'SKU-001',
159
+ title: 'Ürün 1',
160
+ price: 100.0,
161
+ quantity: 1,
162
+ taxRate: 10,
163
+ unit: PPQtyEnums.ADET,
164
+ vatInclude: true,
165
+ productType: PPProductTypeEnum.PHYSICALLY,
166
+ discountValue: 0,
167
+ otvOrani: 0, // opsiyonel, varsayılan: 0
168
+ konaklamaOrani: 0, // opsiyonel, varsayılan: 0
169
+ },
170
+ ],
171
+ transactions: [
172
+ {
173
+ paymentType: PPPaymentType.POS,
174
+ totalAmount: 100.0,
175
+ paymentMethod: PPPaymentMethod.CC,
176
+ },
177
+ ],
178
+ currency: PPCurrencyType.TRY, // opsiyonel, varsayılan: TRY
179
+ deliveryType: PPDeliveryTypeEnum.CASH_ORDER, // opsiyonel, varsayılan: CASH_ORDER
180
+ discountAmount: 0, // opsiyonel, varsayılan: 0
181
+ billingInformation: undefined, // opsiyonel
182
+ installment: undefined, // opsiyonel
183
+ groupCode: undefined, // opsiyonel
184
+ note: undefined, // opsiyonel
185
+ canTryAgain: true, // opsiyonel, varsayılan: true
186
+ });
187
+ ```
188
+
189
+ ## Yanıt Modelleri
190
+
191
+ POS+ yanıtları snake_case JSON olarak döner ve istemci bunları olduğu gibi (hafif tipli nesneler olarak) döndürür. Sık kullanılan alanlar tiplenmiştir; ek alanlara da indeks imzası ile erişilebilir.
192
+
193
+ ```ts
194
+ const res = await pluspay.startPayment(/* ... */);
195
+ res.status; // string
196
+ res.order_code; // string
197
+ res.total_amount; // number
198
+ res.total_paid; // number
199
+ ```
200
+
201
+ | Metod | Önemli alanlar |
202
+ |-------|----------------|
203
+ | `startPayment` / `cancelPayment` / EFT | `status`, `order_code`, `total_amount`, `total_paid`, `amount_due`, `is_partial`, `card_number_masked`, `rrn` |
204
+ | `startOrderPayment` | `status`, `completed`, `order_code`, `grand_total`, `total_paid`, `amount_due`, `results` |
205
+ | `startMultiPayment` | `order_code`, `status`, `grand_total`, `total_paid`, `products`, `transactions` |
206
+ | `triggerEod` | `success`, `results`, `error_message` |
207
+ | `triggerParameters` | `completed`, `results`, `error_message` |
208
+
209
+ ## Enum'lar
210
+
211
+ ### PPPaymentType
212
+
213
+ `POS`, `PAYCELL`, `HEPSIPAY`, `ISTANBULCARD`, `CASH`, `ONLINE`, `BANK_TRANSFER`, `GASTROPAY`, `CIO_CARD`, `IWALLET`, `PAYE`, `MULTINET`, `METROPOL`, `FASTPAY`, `TICKET`, `EDENRED`, `SETCARD`, `SODEXO`, `GETIRPAY`, `TOKENFLEX`, `YEMEKMATIK`, `ON_CREDIT`, `VIRTUAL_POS`, `CUZDANPLUS`
214
+
215
+ ### PPPaymentMethod
216
+
217
+ `CC`, `CASH`, `QR`, `QR_R`, `NFC`, `QUICKCODE`, `MOBILE`, `SWIPE`, `NONE`, `ONLINE`, `TRENDYOL`, `GETIR`, `YEMEKSEPETI`, `MIGROSYEMEK`
218
+
219
+ ### PPEodType
220
+
221
+ `POS`, `CASH`, `BANK_TRANSFER`, `ONLINE`, `OTHER`, `MULTINET`, `SODEXO`, `SETCARD`, `TICKET`, `METROPOL`, `PAYE`, `TOKENFLEX`, `EDENRED`, `CUZDANPLUS`, `IWALLET`
222
+
223
+ ### PPParameterTypes
224
+
225
+ `bank`, `multinet`, `metropol`, `paye`, `iwallet`
226
+
227
+ ### PPPartialPaymentType
228
+
229
+ `AMOUNT`, `PRODUCT`
230
+
231
+ ### PPOrderStatusEnum
232
+
233
+ `CANCEL`, `NOT_RESPONSE`, `WAITING`, `SUCCESS`
234
+
235
+ ### PPDeliveryStatusEnum
236
+
237
+ `WAITING`, `PREPREING`, `READY`, `ONWAY`, `COMPLETE`, `CANCEL`
238
+
239
+ ### PPDeliveryTypeEnum
240
+
241
+ `CASH_ORDER`, `PACKAGE_ORDER`, `TABLE_ORDER`, `TAKE_AWAY`, `TAKE_CLOSE`
242
+
243
+ ### PPCurrencyType
244
+
245
+ `TRY`, `USD`, `EUR`, `GBP`
246
+
247
+ ### PPProductTypeEnum
248
+
249
+ `PHYSICALLY`, `VIRTUAL`, `INFO`, `MD`, `DSN`, `QP`, `KFO`, `COMMISSION`, `HGS`
250
+
251
+ ### PPQtyEnums
252
+
253
+ `ADET`, `KG`, `GR`, `LT`, `MT`, `KOLI`, `PAKET`, `PORSIYON`
254
+
255
+ ### PPDiscountTypeEnum
256
+
257
+ `PERCENTAGE`, `FIXED_AMOUNT`
258
+
259
+ ### PPDocumentTypeEnum
260
+
261
+ `EFATURA`, `EARSIV`, `BILGIFISI`
262
+
263
+ ### PPDocumentStatusEnum
264
+
265
+ `SUCCESS`, `CANCEL`, `FAIL`, `WAITING`, `NONE`
266
+
267
+ ### PPTransactionStatusEnum
268
+
269
+ `SUCCESS`, `CANCEL`, `FAIL`, `NONE`, `WAITING`, `NOT_RESPONSE`
270
+
271
+ ### PPTransactionTypeEnum
272
+
273
+ `START`, `SATIS`, `CANCEL`, `REFUND`
274
+
275
+ ### PPOrderSourceEnum
276
+
277
+ `POS`, `WEB`, `KIOSK`
278
+
279
+ ## Hata Yönetimi
280
+
281
+ POS+ bir hata yanıtı döndürdüğünde (örn. yetersiz bakiye, kullanıcı iptali), istemci `PPA2AException` fırlatır:
282
+
283
+ ```ts
284
+ try {
285
+ const result = await pluspay.startPayment(request);
286
+ } catch (e) {
287
+ if (e instanceof PPA2AException) {
288
+ console.log(e.errorCode); // örn. "PP-A2A-006"
289
+ console.log(e.message); // örn. "İşlem kullanıcı veya sistem tarafından iptal edildi!"
290
+ }
291
+ }
292
+ ```
293
+
294
+ İstemci tarafındaki hatalar için de aynı exception fırlatılır:
295
+
296
+ | Hata Kodu | Açıklama |
297
+ |-----------|----------|
298
+ | `NOT_INITIALIZED` | `initialize()` çağrılmadan istek gönderildi |
299
+ | `LAUNCH_INTENT_ERROR` | POS+ uygulaması başlatılamadı |
300
+ | `PP-A2A-PARSE` | Yanıt JSON'ı ayrıştırılamadı |
301
+ | `PP-A2A-*` | POS+ tarafından döndürülen hata kodları |
302
+
303
+ ## Yaşam Döngüsü
304
+
305
+ ```ts
306
+ function PaymentScreen() {
307
+ const clientRef = useRef<PPA2AClient | null>(null);
308
+
309
+ useEffect(() => {
310
+ const client = new PPA2AClient();
311
+ clientRef.current = client;
312
+ client.initialize();
313
+ return () => {
314
+ client.dispose();
315
+ };
316
+ }, []);
317
+
318
+ // ...
319
+ }
320
+ ```
321
+
322
+ `initialize()` diğer tüm metodlardan önce çağrılmalıdır. Uygulamanın paket bilgilerini alır ve POS+ sonuçları için bir broadcast receiver kaydeder. İstemciye artık ihtiyaç kalmadığında receiver'ı temizlemek için `dispose()` çağrılmalıdır.
323
+
324
+ ## Örnek Uygulama
325
+
326
+ ```sh
327
+ corepack enable
328
+ yarn install
329
+ # Android cihaz/emülatör bağlı ve POS+ kurulu olmalı
330
+ yarn example android
331
+ ```
332
+
333
+ ## Geliştirme
334
+
335
+ ```sh
336
+ yarn typecheck # tip kontrolü
337
+ yarn test # serializasyon/sözleşme birim testleri
338
+ yarn prepare # paketi lib/ altına derle (bob)
339
+ ```
340
+
341
+ ## Gereksinimler
342
+
343
+ - Yalnızca Android
344
+ - Cihazda POS+ uygulaması yüklü olmalıdır
345
+ - Minimum SDK: 24
346
+
347
+ ## Lisans
348
+
349
+ MIT
@@ -0,0 +1,67 @@
1
+ buildscript {
2
+ ext.PluspayA2aReactNative = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return PluspayA2aReactNative[prop]
15
+ }
16
+
17
+ repositories {
18
+ google()
19
+ mavenCentral()
20
+ }
21
+
22
+ dependencies {
23
+ classpath "com.android.tools.build:gradle:8.7.2"
24
+ // noinspection DifferentKotlinGradleVersion
25
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+
30
+ apply plugin: "com.android.library"
31
+ apply plugin: "kotlin-android"
32
+
33
+ apply plugin: "com.facebook.react"
34
+
35
+ android {
36
+ namespace "com.pluspaya2areactnative"
37
+
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
43
+ }
44
+
45
+ buildFeatures {
46
+ buildConfig true
47
+ }
48
+
49
+ buildTypes {
50
+ release {
51
+ minifyEnabled false
52
+ }
53
+ }
54
+
55
+ lint {
56
+ disable "GradleCompatible"
57
+ }
58
+
59
+ compileOptions {
60
+ sourceCompatibility JavaVersion.VERSION_1_8
61
+ targetCompatibility JavaVersion.VERSION_1_8
62
+ }
63
+ }
64
+
65
+ dependencies {
66
+ implementation "com.facebook.react:react-android"
67
+ }
@@ -0,0 +1,11 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <!--
3
+ Android 11+ (API 30) package visibility: declare that this library needs
4
+ to resolve/launch the POS+ App2App intent. Merged into the host app.
5
+ -->
6
+ <queries>
7
+ <intent>
8
+ <action android:name="com.pluspay.POS_A2A" />
9
+ </intent>
10
+ </queries>
11
+ </manifest>
@@ -0,0 +1,115 @@
1
+ package com.pluspaya2areactnative
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.content.IntentFilter
7
+ import android.os.Build
8
+ import android.util.Log
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.ReactApplicationContext
11
+ import org.json.JSONObject
12
+
13
+ /**
14
+ * Thin native bridge for PlusPay POS+ App2App (A2A).
15
+ *
16
+ * It only transports raw JSON. Each [sendRequest] launches POS+ via an intent
17
+ * and resolves the JS Promise when POS+ broadcasts back the result. All models
18
+ * / serialization live in TypeScript.
19
+ */
20
+ class PluspayA2aReactNativeModule(private val reactContext: ReactApplicationContext) :
21
+ NativePluspayA2aReactNativeSpec(reactContext) {
22
+
23
+ private var receiver: BroadcastReceiver? = null
24
+ private var pendingPromise: Promise? = null
25
+
26
+ private val resultAction: String
27
+ get() = "${reactContext.packageName}.PLUSPAY_A2A_RESULT"
28
+
29
+ override fun getName(): String = NAME
30
+
31
+ override fun initialize(promise: Promise) {
32
+ try {
33
+ if (receiver == null) {
34
+ receiver = object : BroadcastReceiver() {
35
+ override fun onReceive(context: Context?, intent: Intent?) {
36
+ if (intent?.action != resultAction) return
37
+ val response = intent.getStringExtra("response") ?: "{}"
38
+ val p = pendingPromise
39
+ pendingPromise = null
40
+ p?.resolve(response)
41
+ }
42
+ }
43
+ val filter = IntentFilter(resultAction)
44
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
45
+ reactContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
46
+ } else {
47
+ @Suppress("UnspecifiedRegisterReceiverFlag")
48
+ reactContext.registerReceiver(receiver, filter)
49
+ }
50
+ }
51
+
52
+ val info = JSONObject().apply {
53
+ put("packageName", reactContext.packageName)
54
+ put("activityName", reactContext.currentActivity?.componentName?.className ?: "")
55
+ }
56
+ promise.resolve(info.toString())
57
+ } catch (e: Exception) {
58
+ Log.e(TAG, "initialize failed", e)
59
+ promise.reject("INIT_ERROR", e.message, e)
60
+ }
61
+ }
62
+
63
+ override fun sendRequest(requestJson: String, promise: Promise) {
64
+ // Only one request can be in flight; supersede any previous pending one.
65
+ pendingPromise?.let { prev ->
66
+ pendingPromise = null
67
+ prev.reject("SUPERSEDED", "Request superseded by a new request.")
68
+ }
69
+ pendingPromise = promise
70
+
71
+ try {
72
+ val activity = reactContext.currentActivity
73
+ val intent = Intent(ACTION_POS_A2A).apply {
74
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or
75
+ Intent.FLAG_ACTIVITY_CLEAR_TOP or
76
+ Intent.FLAG_ACTIVITY_SINGLE_TOP
77
+ putExtra("request", requestJson)
78
+ putExtra("resultAction", resultAction)
79
+ putExtra("callerPackage", reactContext.packageName)
80
+ putExtra("callerActivity", activity?.javaClass?.name ?: "")
81
+ }
82
+ if (activity != null) {
83
+ activity.startActivity(intent)
84
+ } else {
85
+ reactContext.startActivity(intent)
86
+ }
87
+ } catch (e: Exception) {
88
+ pendingPromise = null
89
+ promise.reject("LAUNCH_INTENT_ERROR", e.message, e)
90
+ }
91
+ }
92
+
93
+ override fun dispose(promise: Promise) {
94
+ try {
95
+ receiver?.let {
96
+ try {
97
+ reactContext.unregisterReceiver(it)
98
+ } catch (_: Exception) {
99
+ // already unregistered
100
+ }
101
+ }
102
+ receiver = null
103
+ pendingPromise = null
104
+ promise.resolve(null)
105
+ } catch (e: Exception) {
106
+ promise.reject("DISPOSE_ERROR", e.message, e)
107
+ }
108
+ }
109
+
110
+ companion object {
111
+ const val NAME = NativePluspayA2aReactNativeSpec.NAME
112
+ private const val ACTION_POS_A2A = "com.pluspay.POS_A2A"
113
+ private const val TAG = "PluspayA2aRN"
114
+ }
115
+ }
@@ -0,0 +1,31 @@
1
+ package com.pluspaya2areactnative
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class PluspayA2aReactNativePackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == PluspayA2aReactNativeModule.NAME) {
13
+ PluspayA2aReactNativeModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
+ mapOf(
21
+ PluspayA2aReactNativeModule.NAME to ReactModuleInfo(
22
+ name = PluspayA2aReactNativeModule.NAME,
23
+ className = PluspayA2aReactNativeModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ #import <PluspayA2aReactNativeSpec/PluspayA2aReactNativeSpec.h>
2
+
3
+ @interface PluspayA2aReactNative : NSObject <NativePluspayA2aReactNativeSpec>
4
+
5
+ @end
@@ -0,0 +1,37 @@
1
+ #import "PluspayA2aReactNative.h"
2
+
3
+ // PlusPay POS+ App2App (A2A) is Android-only (Android intents). On iOS every
4
+ // method rejects with UNSUPPORTED_PLATFORM so the package still builds.
5
+ @implementation PluspayA2aReactNative
6
+
7
+ - (void)initialize:(RCTPromiseResolveBlock)resolve
8
+ reject:(RCTPromiseRejectBlock)reject
9
+ {
10
+ reject(@"UNSUPPORTED_PLATFORM", @"PlusPay A2A is only supported on Android.", nil);
11
+ }
12
+
13
+ - (void)sendRequest:(NSString *)requestJson
14
+ resolve:(RCTPromiseResolveBlock)resolve
15
+ reject:(RCTPromiseRejectBlock)reject
16
+ {
17
+ reject(@"UNSUPPORTED_PLATFORM", @"PlusPay A2A is only supported on Android.", nil);
18
+ }
19
+
20
+ - (void)dispose:(RCTPromiseResolveBlock)resolve
21
+ reject:(RCTPromiseRejectBlock)reject
22
+ {
23
+ resolve(nil);
24
+ }
25
+
26
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
27
+ (const facebook::react::ObjCTurboModule::InitParams &)params
28
+ {
29
+ return std::make_shared<facebook::react::NativePluspayA2aReactNativeSpecJSI>(params);
30
+ }
31
+
32
+ + (NSString *)moduleName
33
+ {
34
+ return @"PluspayA2aReactNative";
35
+ }
36
+
37
+ @end
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+
5
+ /**
6
+ * Thin native bridge for PlusPay POS+ App2App (A2A).
7
+ *
8
+ * The bridge only transports raw JSON: it sends the A2A intent to POS+ and
9
+ * resolves with the raw response JSON received via BroadcastReceiver.
10
+ * All models / serialization live in TypeScript (see ./models).
11
+ */
12
+
13
+ export default TurboModuleRegistry.getEnforcing('PluspayA2aReactNative');
14
+ //# sourceMappingURL=NativePluspayA2aReactNative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativePluspayA2aReactNative.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;;AAkBA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,uBAAuB,CAAC","ignoreList":[]}