@capivv/capacitor-sdk 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/CapivvCapacitor.podspec +20 -0
- package/README.md +378 -0
- package/android/build.gradle +74 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/capivv/capacitor/CapivvPlugin.kt +708 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.js +9 -0
- package/dist/esm/components/index.js.map +1 -0
- package/dist/esm/definitions.d.ts +327 -0
- package/dist/esm/definitions.js +20 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/l10n/translations.d.ts +73 -0
- package/dist/esm/l10n/translations.js +397 -0
- package/dist/esm/l10n/translations.js.map +1 -0
- package/dist/esm/templates/types.d.ts +118 -0
- package/dist/esm/templates/types.js +8 -0
- package/dist/esm/templates/types.js.map +1 -0
- package/dist/esm/web.d.ts +72 -0
- package/dist/esm/web.js +230 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +661 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +664 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/CapivvPlugin.m +21 -0
- package/ios/Plugin/CapivvPlugin.swift +655 -0
- package/package.json +89 -0
|
@@ -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 = 'CapivvCapacitor'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url'].gsub(/^git\+/, '').gsub(/\.git$/, '')
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'].gsub(/^git\+/, ''), :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '15.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.9'
|
|
17
|
+
|
|
18
|
+
# Required frameworks for StoreKit 2
|
|
19
|
+
s.frameworks = 'StoreKit'
|
|
20
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @capivv/capacitor-sdk
|
|
2
|
+
|
|
3
|
+
Official Capacitor plugin for [Capivv](https://capivv.com) - Revenue as Code for subscription management.
|
|
4
|
+
|
|
5
|
+
This plugin provides in-app purchase and subscription management for Ionic/Capacitor apps, with native support for iOS (StoreKit 2) and Android (Google Play Billing).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @capivv/capacitor-sdk
|
|
11
|
+
npx cap sync
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Platform Requirements
|
|
15
|
+
|
|
16
|
+
- **iOS**: iOS 15.0+ (StoreKit 2)
|
|
17
|
+
- **Android**: API 24+ (Google Play Billing Library 6.x)
|
|
18
|
+
- **Web**: Requires Stripe integration (coming soon)
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
### iOS Setup
|
|
23
|
+
|
|
24
|
+
Add in-app purchase capability in Xcode:
|
|
25
|
+
|
|
26
|
+
1. Open your app in Xcode
|
|
27
|
+
2. Select your target → "Signing & Capabilities"
|
|
28
|
+
3. Click "+ Capability" and add "In-App Purchase"
|
|
29
|
+
|
|
30
|
+
### Android Setup
|
|
31
|
+
|
|
32
|
+
No additional configuration required. The plugin automatically requests the billing permission.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Initialize the SDK
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Capivv } from '@capivv/capacitor-sdk';
|
|
40
|
+
|
|
41
|
+
// Configure with your API key
|
|
42
|
+
await Capivv.configure({
|
|
43
|
+
apiKey: 'capivv_pk_your_public_key',
|
|
44
|
+
debug: true, // Enable for development
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Identify the current user
|
|
48
|
+
const userInfo = await Capivv.identify({
|
|
49
|
+
userId: 'user_123',
|
|
50
|
+
attributes: {
|
|
51
|
+
email: 'user@example.com',
|
|
52
|
+
name: 'John Doe',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log('User entitlements:', userInfo.entitlements);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Check Entitlements
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Check if user has access to a specific feature
|
|
63
|
+
const result = await Capivv.checkEntitlement({
|
|
64
|
+
entitlementIdentifier: 'premium',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (result.hasAccess) {
|
|
68
|
+
// User has premium access
|
|
69
|
+
showPremiumContent();
|
|
70
|
+
} else {
|
|
71
|
+
// Show paywall
|
|
72
|
+
showPaywall();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Get all active entitlements
|
|
76
|
+
const { entitlements } = await Capivv.getEntitlements();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Get Products and Offerings
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Get configured offerings from Capivv dashboard
|
|
83
|
+
const { offerings } = await Capivv.getOfferings();
|
|
84
|
+
|
|
85
|
+
for (const offering of offerings) {
|
|
86
|
+
console.log(`Offering: ${offering.identifier}`);
|
|
87
|
+
for (const product of offering.products) {
|
|
88
|
+
console.log(` ${product.title}: ${product.priceString}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get a specific product
|
|
93
|
+
const { product } = await Capivv.getProduct({
|
|
94
|
+
productIdentifier: 'com.yourapp.premium.monthly',
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Make a Purchase
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { Capivv, ProductType } from '@capivv/capacitor-sdk';
|
|
102
|
+
|
|
103
|
+
const result = await Capivv.purchase({
|
|
104
|
+
productIdentifier: 'com.yourapp.premium.monthly',
|
|
105
|
+
productType: ProductType.SUBSCRIPTION,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (result.success) {
|
|
109
|
+
console.log('Purchase successful!', result.transaction);
|
|
110
|
+
// Entitlements are automatically updated
|
|
111
|
+
} else {
|
|
112
|
+
console.log('Purchase failed:', result.error);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Restore Purchases
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Restore purchases for users who reinstall or switch devices
|
|
120
|
+
const { entitlements } = await Capivv.restorePurchases();
|
|
121
|
+
console.log('Restored entitlements:', entitlements);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Listen for Updates
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Listen for entitlement changes
|
|
128
|
+
const listener = await Capivv.addListener('entitlementsUpdated', (event) => {
|
|
129
|
+
console.log('Entitlements updated:', event.entitlements);
|
|
130
|
+
updateUIForEntitlements(event.entitlements);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Listen for purchase completion
|
|
134
|
+
await Capivv.addListener('purchaseCompleted', (event) => {
|
|
135
|
+
console.log('Purchase completed:', event.transaction);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Listen for purchase failures
|
|
139
|
+
await Capivv.addListener('purchaseFailed', (event) => {
|
|
140
|
+
console.error('Purchase failed:', event.error);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Remove listener when done
|
|
144
|
+
listener.remove();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Subscription Management
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Open platform-specific subscription management UI
|
|
151
|
+
await Capivv.manageSubscriptions();
|
|
152
|
+
// iOS: Opens App Store subscription settings
|
|
153
|
+
// Android: Opens Google Play subscription management
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Sync Purchases
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Sync local purchases with server (call on app launch)
|
|
160
|
+
const { entitlements } = await Capivv.syncPurchases();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### Configuration
|
|
166
|
+
|
|
167
|
+
#### `configure(config: CapivvConfig): Promise<void>`
|
|
168
|
+
|
|
169
|
+
Initialize the SDK with your API key.
|
|
170
|
+
|
|
171
|
+
| Parameter | Type | Required | Description |
|
|
172
|
+
| --------- | ------- | -------- | ---------------------------------------- |
|
|
173
|
+
| apiKey | string | Yes | Your Capivv public API key |
|
|
174
|
+
| apiUrl | string | No | API endpoint (defaults to production) |
|
|
175
|
+
| debug | boolean | No | Enable debug logging |
|
|
176
|
+
|
|
177
|
+
### User Management
|
|
178
|
+
|
|
179
|
+
#### `identify(options): Promise<UserInfo>`
|
|
180
|
+
|
|
181
|
+
Identify the current user. Creates the user in Capivv if they don't exist.
|
|
182
|
+
|
|
183
|
+
#### `logout(): Promise<void>`
|
|
184
|
+
|
|
185
|
+
Log out the current user and clear cached data.
|
|
186
|
+
|
|
187
|
+
#### `getUserInfo(): Promise<UserInfo>`
|
|
188
|
+
|
|
189
|
+
Get the current user's information including entitlements.
|
|
190
|
+
|
|
191
|
+
### Products
|
|
192
|
+
|
|
193
|
+
#### `getOfferings(): Promise<{ offerings: Offering[] }>`
|
|
194
|
+
|
|
195
|
+
Get available offerings configured in your Capivv dashboard.
|
|
196
|
+
|
|
197
|
+
#### `getProduct(options): Promise<{ product: Product }>`
|
|
198
|
+
|
|
199
|
+
Get a specific product by identifier.
|
|
200
|
+
|
|
201
|
+
#### `getProducts(options): Promise<{ products: Product[] }>`
|
|
202
|
+
|
|
203
|
+
Get multiple products by identifier.
|
|
204
|
+
|
|
205
|
+
### Purchases
|
|
206
|
+
|
|
207
|
+
#### `purchase(options): Promise<PurchaseResult>`
|
|
208
|
+
|
|
209
|
+
Purchase a product.
|
|
210
|
+
|
|
211
|
+
#### `restorePurchases(): Promise<{ entitlements: Entitlement[] }>`
|
|
212
|
+
|
|
213
|
+
Restore previous purchases.
|
|
214
|
+
|
|
215
|
+
#### `syncPurchases(): Promise<{ entitlements: Entitlement[] }>`
|
|
216
|
+
|
|
217
|
+
Sync purchases with the server.
|
|
218
|
+
|
|
219
|
+
#### `manageSubscriptions(): Promise<void>`
|
|
220
|
+
|
|
221
|
+
Open the platform-specific subscription management UI.
|
|
222
|
+
|
|
223
|
+
### Entitlements
|
|
224
|
+
|
|
225
|
+
#### `checkEntitlement(options): Promise<EntitlementCheckResult>`
|
|
226
|
+
|
|
227
|
+
Check if the user has access to a specific entitlement.
|
|
228
|
+
|
|
229
|
+
#### `getEntitlements(): Promise<{ entitlements: Entitlement[] }>`
|
|
230
|
+
|
|
231
|
+
Get all active entitlements for the current user.
|
|
232
|
+
|
|
233
|
+
### Events
|
|
234
|
+
|
|
235
|
+
| Event | Description |
|
|
236
|
+
| -------------------- | ------------------------------------------ |
|
|
237
|
+
| entitlementsUpdated | Fired when entitlements change |
|
|
238
|
+
| purchaseCompleted | Fired when a purchase completes |
|
|
239
|
+
| purchaseFailed | Fired when a purchase fails |
|
|
240
|
+
|
|
241
|
+
## Integration with Ionic
|
|
242
|
+
|
|
243
|
+
### Angular
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { Component, OnInit } from '@angular/core';
|
|
247
|
+
import { Capivv } from '@capivv/capacitor-sdk';
|
|
248
|
+
|
|
249
|
+
@Component({
|
|
250
|
+
selector: 'app-subscription',
|
|
251
|
+
template: `
|
|
252
|
+
<ion-content>
|
|
253
|
+
<ion-list *ngIf="offerings">
|
|
254
|
+
<ion-item *ngFor="let product of offerings[0]?.products" (click)="purchase(product)">
|
|
255
|
+
<ion-label>
|
|
256
|
+
<h2>{{ product.title }}</h2>
|
|
257
|
+
<p>{{ product.priceString }}</p>
|
|
258
|
+
</ion-label>
|
|
259
|
+
</ion-item>
|
|
260
|
+
</ion-list>
|
|
261
|
+
</ion-content>
|
|
262
|
+
`,
|
|
263
|
+
})
|
|
264
|
+
export class SubscriptionPage implements OnInit {
|
|
265
|
+
offerings: Offering[] = [];
|
|
266
|
+
|
|
267
|
+
async ngOnInit() {
|
|
268
|
+
await Capivv.configure({ apiKey: 'your_api_key' });
|
|
269
|
+
await Capivv.identify({ userId: 'user_123' });
|
|
270
|
+
|
|
271
|
+
const { offerings } = await Capivv.getOfferings();
|
|
272
|
+
this.offerings = offerings;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async purchase(product: Product) {
|
|
276
|
+
const result = await Capivv.purchase({
|
|
277
|
+
productIdentifier: product.identifier,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (result.success) {
|
|
281
|
+
// Navigate to premium content
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### React
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { useEffect, useState } from 'react';
|
|
291
|
+
import { Capivv, Offering } from '@capivv/capacitor-sdk';
|
|
292
|
+
|
|
293
|
+
function SubscriptionPage() {
|
|
294
|
+
const [offerings, setOfferings] = useState<Offering[]>([]);
|
|
295
|
+
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
async function init() {
|
|
298
|
+
await Capivv.configure({ apiKey: 'your_api_key' });
|
|
299
|
+
await Capivv.identify({ userId: 'user_123' });
|
|
300
|
+
|
|
301
|
+
const { offerings } = await Capivv.getOfferings();
|
|
302
|
+
setOfferings(offerings);
|
|
303
|
+
}
|
|
304
|
+
init();
|
|
305
|
+
}, []);
|
|
306
|
+
|
|
307
|
+
const handlePurchase = async (productId: string) => {
|
|
308
|
+
const result = await Capivv.purchase({ productIdentifier: productId });
|
|
309
|
+
if (result.success) {
|
|
310
|
+
// Navigate to premium content
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
return (
|
|
315
|
+
<IonContent>
|
|
316
|
+
<IonList>
|
|
317
|
+
{offerings[0]?.products.map((product) => (
|
|
318
|
+
<IonItem key={product.identifier} onClick={() => handlePurchase(product.identifier)}>
|
|
319
|
+
<IonLabel>
|
|
320
|
+
<h2>{product.title}</h2>
|
|
321
|
+
<p>{product.priceString}</p>
|
|
322
|
+
</IonLabel>
|
|
323
|
+
</IonItem>
|
|
324
|
+
))}
|
|
325
|
+
</IonList>
|
|
326
|
+
</IonContent>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Testing
|
|
332
|
+
|
|
333
|
+
### iOS Sandbox Testing
|
|
334
|
+
|
|
335
|
+
1. Create sandbox testers in App Store Connect
|
|
336
|
+
2. Sign out of your Apple ID on the device
|
|
337
|
+
3. Launch your app and attempt a purchase
|
|
338
|
+
4. Sign in with the sandbox tester account when prompted
|
|
339
|
+
|
|
340
|
+
### Android Test Purchases
|
|
341
|
+
|
|
342
|
+
1. Add license testers in Google Play Console
|
|
343
|
+
2. Upload your app to internal testing track
|
|
344
|
+
3. Use the license tester accounts to test purchases
|
|
345
|
+
|
|
346
|
+
## Troubleshooting
|
|
347
|
+
|
|
348
|
+
### "Not configured" error
|
|
349
|
+
|
|
350
|
+
Make sure to call `configure()` before any other methods:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
await Capivv.configure({ apiKey: 'your_api_key' });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### "Not identified" error
|
|
357
|
+
|
|
358
|
+
Make sure to call `identify()` after configuration:
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
await Capivv.identify({ userId: 'user_123' });
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Products not loading
|
|
365
|
+
|
|
366
|
+
- Verify products are configured in App Store Connect / Google Play Console
|
|
367
|
+
- Ensure product IDs match exactly (case-sensitive)
|
|
368
|
+
- For iOS, make sure paid agreements are signed in App Store Connect
|
|
369
|
+
- For Android, ensure the app is uploaded to at least internal testing
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
MIT
|
|
374
|
+
|
|
375
|
+
## Support
|
|
376
|
+
|
|
377
|
+
- Documentation: https://docs.capivv.com
|
|
378
|
+
- Issues: https://github.com/capivv/capivv/issues
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '1.9.22'
|
|
10
|
+
repositories {
|
|
11
|
+
google()
|
|
12
|
+
mavenCentral()
|
|
13
|
+
}
|
|
14
|
+
dependencies {
|
|
15
|
+
classpath 'com.android.tools.build:gradle:8.2.1'
|
|
16
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
apply plugin: 'com.android.library'
|
|
21
|
+
apply plugin: 'kotlin-android'
|
|
22
|
+
|
|
23
|
+
android {
|
|
24
|
+
namespace "com.capivv.capacitor"
|
|
25
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
28
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
|
29
|
+
versionCode 1
|
|
30
|
+
versionName "0.1.0"
|
|
31
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
32
|
+
}
|
|
33
|
+
buildTypes {
|
|
34
|
+
release {
|
|
35
|
+
minifyEnabled false
|
|
36
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
compileOptions {
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
42
|
+
}
|
|
43
|
+
kotlinOptions {
|
|
44
|
+
jvmTarget = '17'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
repositories {
|
|
49
|
+
google()
|
|
50
|
+
mavenCentral()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dependencies {
|
|
54
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
55
|
+
implementation project(':capacitor-android')
|
|
56
|
+
|
|
57
|
+
// Google Play Billing
|
|
58
|
+
implementation 'com.android.billingclient:billing-ktx:6.1.0'
|
|
59
|
+
|
|
60
|
+
// Kotlin Coroutines
|
|
61
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
|
62
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3'
|
|
63
|
+
|
|
64
|
+
// JSON
|
|
65
|
+
implementation 'org.json:json:20231013'
|
|
66
|
+
|
|
67
|
+
// OkHttp for API requests
|
|
68
|
+
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
|
69
|
+
|
|
70
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
71
|
+
testImplementation "junit:junit:$junitVersion"
|
|
72
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
73
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
74
|
+
}
|