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.
- package/LICENSE +20 -0
- package/PluspayA2aReactNative.podspec +20 -0
- package/README.md +349 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +11 -0
- package/android/src/main/java/com/pluspaya2areactnative/PluspayA2aReactNativeModule.kt +115 -0
- package/android/src/main/java/com/pluspaya2areactnative/PluspayA2aReactNativePackage.kt +31 -0
- package/ios/PluspayA2aReactNative.h +5 -0
- package/ios/PluspayA2aReactNative.mm +37 -0
- package/lib/module/NativePluspayA2aReactNative.js +14 -0
- package/lib/module/NativePluspayA2aReactNative.js.map +1 -0
- package/lib/module/client.js +112 -0
- package/lib/module/client.js.map +1 -0
- package/lib/module/index.js +8 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/models/enums.js +190 -0
- package/lib/module/models/enums.js.map +1 -0
- package/lib/module/models/exceptions.js +19 -0
- package/lib/module/models/exceptions.js.map +1 -0
- package/lib/module/models/requests.js +197 -0
- package/lib/module/models/requests.js.map +1 -0
- package/lib/module/models/responses.js +2 -0
- package/lib/module/models/responses.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativePluspayA2aReactNative.d.ts +25 -0
- package/lib/typescript/src/NativePluspayA2aReactNative.d.ts.map +1 -0
- package/lib/typescript/src/client.d.ts +50 -0
- package/lib/typescript/src/client.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/models/enums.d.ts +169 -0
- package/lib/typescript/src/models/enums.d.ts.map +1 -0
- package/lib/typescript/src/models/exceptions.d.ts +10 -0
- package/lib/typescript/src/models/exceptions.d.ts.map +1 -0
- package/lib/typescript/src/models/requests.d.ts +128 -0
- package/lib/typescript/src/models/requests.d.ts.map +1 -0
- package/lib/typescript/src/models/responses.d.ts +51 -0
- package/lib/typescript/src/models/responses.d.ts.map +1 -0
- package/package.json +127 -0
- package/src/NativePluspayA2aReactNative.ts +27 -0
- package/src/client.ts +137 -0
- package/src/index.tsx +5 -0
- package/src/models/enums.ts +186 -0
- package/src/models/exceptions.ts +19 -0
- package/src/models/requests.ts +302 -0
- 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,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":[]}
|