Skip to content

Commit 7e8ec62

Browse files
authored
[iOS + Android] Virtual Currency Support (#502)
1 parent 36a2955 commit 7e8ec62

File tree

7 files changed

+189
-0
lines changed

7 files changed

+189
-0
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ This plugin is based on [CapGo's Capacitor plugin](https://www.npmjs.com/package
5454
<docgen-index>
5555

5656
* [`configure(...)`](#configure)
57+
* [`getVirtualCurrencies()`](#getvirtualcurrencies)
58+
* [`invalidateVirtualCurrenciesCache()`](#invalidatevirtualcurrenciescache)
59+
* [`getCachedVirtualCurrencies()`](#getcachedvirtualcurrencies)
5760
* [`parseAsWebPurchaseRedemption(...)`](#parseaswebpurchaseredemption)
5861
* [`redeemWebPurchase(...)`](#redeemwebpurchase)
5962
* [`setMockWebResults(...)`](#setmockwebresults)
@@ -144,6 +147,49 @@ Sets up Purchases with your API key and an app user id.
144147
--------------------
145148

146149

150+
### getVirtualCurrencies()
151+
152+
```typescript
153+
getVirtualCurrencies() => Promise<{ virtualCurrencies: PurchasesVirtualCurrencies; }>
154+
```
155+
156+
Fetches the virtual currencies for the current subscriber.
157+
158+
**Returns:** <code>Promise&lt;{ virtualCurrencies: <a href="#purchasesvirtualcurrencies">PurchasesVirtualCurrencies</a>; }&gt;</code>
159+
160+
--------------------
161+
162+
163+
### invalidateVirtualCurrenciesCache()
164+
165+
```typescript
166+
invalidateVirtualCurrenciesCache() => Promise<void>
167+
```
168+
169+
Invalidates the cache for virtual currencies.
170+
171+
This is useful for cases where a virtual currency's balance might have been updated
172+
outside of the app, like if you decreased a user's balance from the user spending a virtual currency,
173+
or if you increased the balance from your backend using the server APIs.
174+
175+
--------------------
176+
177+
178+
### getCachedVirtualCurrencies()
179+
180+
```typescript
181+
getCachedVirtualCurrencies() => Promise<{ cachedVirtualCurrencies: PurchasesVirtualCurrencies | null; }>
182+
```
183+
184+
The currently cached {@link <a href="#purchasesvirtualcurrencies">PurchasesVirtualCurrencies</a>} if one is available.
185+
This value will remain null until virtual currencies have been fetched at
186+
least once with {@link getVirtualCurrencies} or an equivalent function.
187+
188+
**Returns:** <code>Promise&lt;{ cachedVirtualCurrencies: <a href="#purchasesvirtualcurrencies">PurchasesVirtualCurrencies</a> | null; }&gt;</code>
189+
190+
--------------------
191+
192+
147193
### parseAsWebPurchaseRedemption(...)
148194

149195
```typescript
@@ -1229,6 +1275,28 @@ Holds parameters to initialize the SDK.
12291275
| **`diagnosticsEnabled`** | <code>boolean</code> | Enabling diagnostics will send some performance and debugging information from the SDK to RevenueCat's servers. Examples of this information include response times, cache hits or error codes. No personal identifiable information will be collected. The default value is false. |
12301276

12311277

1278+
#### PurchasesVirtualCurrencies
1279+
1280+
The VirtualCurrencies object contains all the virtual currencies associated to the user.
1281+
1282+
| Prop | Type | Description |
1283+
| --------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
1284+
| **`all`** | <code>{ [key: string]: <a href="#purchasesvirtualcurrency">PurchasesVirtualCurrency</a>; }</code> | Map of all VirtualCurrency (<a href="#purchasesvirtualcurrency">`PurchasesVirtualCurrency`</a>) objects keyed by virtual currency code. |
1285+
1286+
1287+
#### PurchasesVirtualCurrency
1288+
1289+
The VirtualCurrency object represents information about a virtual currency in the app.
1290+
Use this object to access information about a virtual currency, such as its current balance.
1291+
1292+
| Prop | Type | Description |
1293+
| ----------------------- | --------------------------- | ----------------------------------------------------------------------- |
1294+
| **`balance`** | <code>number</code> | The virtual currency's balance. |
1295+
| **`name`** | <code>string</code> | The virtual currency's name. |
1296+
| **`code`** | <code>string</code> | The virtual currency's code. |
1297+
| **`serverDescription`** | <code>string \| null</code> | The virtual currency's description defined in the RevenueCat dashboard. |
1298+
1299+
12321300
#### CustomerInfo
12331301

12341302
Type containing all information regarding the customer

android/src/main/java/com/revenuecat/purchases/capacitor/PurchasesPlugin.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@ import com.revenuecat.purchases.hybridcommon.canMakePayments as canMakePaymentsC
3232
import com.revenuecat.purchases.hybridcommon.checkTrialOrIntroductoryPriceEligibility as checkTrialOrIntroductoryPriceEligibilityCommon
3333
import com.revenuecat.purchases.hybridcommon.collectDeviceIdentifiers as collectDeviceIdentifiersCommon
3434
import com.revenuecat.purchases.hybridcommon.getAppUserID as getAppUserIDCommon
35+
import com.revenuecat.purchases.hybridcommon.getCachedVirtualCurrencies as getCachedVirtualCurrenciesCommon
3536
import com.revenuecat.purchases.hybridcommon.getCurrentOfferingForPlacement as getCurrentOfferingForPlacementCommon
3637
import com.revenuecat.purchases.hybridcommon.getCustomerInfo as getCustomerInfoCommon
3738
import com.revenuecat.purchases.hybridcommon.getOfferings as getOfferingsCommon
3839
import com.revenuecat.purchases.hybridcommon.getStorefront as getStorefrontCommon
40+
import com.revenuecat.purchases.hybridcommon.getVirtualCurrencies as getVirtualCurrenciesCommon
3941
import com.revenuecat.purchases.hybridcommon.invalidateCustomerInfoCache as invalidateCustomerInfoCacheCommon
42+
import com.revenuecat.purchases.hybridcommon.invalidateVirtualCurrenciesCache as invalidateVirtualCurrenciesCacheCommon
4043
import com.revenuecat.purchases.hybridcommon.isAnonymous as isAnonymousCommon
4144
import com.revenuecat.purchases.hybridcommon.logIn as logInCommon
4245
import com.revenuecat.purchases.hybridcommon.logOut as logOutCommon
@@ -125,6 +128,25 @@ class PurchasesPlugin : Plugin() {
125128
call.resolve()
126129
}
127130

131+
@PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
132+
fun getVirtualCurrencies(call: PluginCall) {
133+
if (rejectIfNotConfigured(call)) return
134+
getVirtualCurrenciesCommon(getOnResult(call, "virtualCurrencies"))
135+
}
136+
137+
@PluginMethod(returnType = PluginMethod.RETURN_NONE)
138+
fun invalidateVirtualCurrenciesCache(call: PluginCall) {
139+
if (rejectIfNotConfigured(call)) return
140+
invalidateVirtualCurrenciesCacheCommon()
141+
call.resolve()
142+
}
143+
144+
@PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
145+
fun getCachedVirtualCurrencies(call: PluginCall) {
146+
if (rejectIfNotConfigured(call)) return
147+
call.resolveWithMap(mapOf("cachedVirtualCurrencies" to getCachedVirtualCurrenciesCommon()))
148+
}
149+
128150
@PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
129151
fun parseAsWebPurchaseRedemption(call: PluginCall) {
130152
val urlString = call.getStringOrReject("urlString") ?: return

example/purchase-tester/src/components/FunctionTesterContainer.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,21 @@ const FunctionTesterContainer: React.FC<ContainerProps> = () => {
540540
updateLastFunction('isConfigured', isConfiguredResult);
541541
};
542542

543+
const getVirtualCurrencies = async () => {
544+
const virtualCurrencies = await Purchases.getVirtualCurrencies();
545+
updateLastFunction('getVirtualCurrencies', virtualCurrencies);
546+
};
547+
548+
const invalidateVirtualCurrenciesCache = async () => {
549+
await Purchases.invalidateVirtualCurrenciesCache();
550+
updateLastFunctionWithoutContent('invalidateVirtualCurrenciesCache');
551+
};
552+
553+
const getCachedVirtualCurrencies = async () => {
554+
const cachedVirtualCurrencies = await Purchases.getCachedVirtualCurrencies();
555+
updateLastFunction('getCachedVirtualCurrencies', cachedVirtualCurrencies);
556+
};
557+
543558
const purchaseProductForWinBackTesting = async () => {
544559
try {
545560
const products = await Purchases.getProducts({
@@ -946,6 +961,15 @@ const FunctionTesterContainer: React.FC<ContainerProps> = () => {
946961
<IonButton size="small" onClick={isConfigured}>
947962
Is configured?
948963
</IonButton>
964+
<IonButton size="small" onClick={getVirtualCurrencies}>
965+
Get virtual currencies
966+
</IonButton>
967+
<IonButton size="small" onClick={invalidateVirtualCurrenciesCache}>
968+
Invalidate virtual currencies cache
969+
</IonButton>
970+
<IonButton size="small" onClick={getCachedVirtualCurrencies}>
971+
Get cached virtual currencies
972+
</IonButton>
949973
<IonButton size="small" onClick={purchaseProductForWinBackTesting}>
950974
Purchase Product for WinBack Testing
951975
</IonButton>

ios/Plugin/PurchasesPlugin.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// each method the plugin supports using the CAP_PLUGIN_METHOD macro.
66
CAP_PLUGIN(PurchasesPlugin, "Purchases",
77
CAP_PLUGIN_METHOD(configure, CAPPluginReturnNone);
8+
CAP_PLUGIN_METHOD(getVirtualCurrencies, CAPPluginReturnPromise);
9+
CAP_PLUGIN_METHOD(invalidateVirtualCurrenciesCache, CAPPluginReturnNone);
10+
CAP_PLUGIN_METHOD(getCachedVirtualCurrencies, CAPPluginReturnPromise);
811
CAP_PLUGIN_METHOD(parseAsWebPurchaseRedemption, CAPPluginReturnPromise);
912
CAP_PLUGIN_METHOD(redeemWebPurchase, CAPPluginReturnPromise);
1013
CAP_PLUGIN_METHOD(setMockWebResults, CAPPluginReturnNone);

ios/Plugin/PurchasesPlugin.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,29 @@ public class PurchasesPlugin: CAPPlugin, PurchasesDelegate {
6969
call.resolve()
7070
}
7171

72+
@objc func getVirtualCurrencies(_ call: CAPPluginCall) {
73+
guard self.rejectIfPurchasesNotConfigured(call) else { return }
74+
CommonFunctionality.getVirtualCurrencies(
75+
completion: self.getCompletionBlockHandler(
76+
call,
77+
wrapperKey: "virtualCurrencies"
78+
)
79+
)
80+
}
81+
82+
@objc func invalidateVirtualCurrenciesCache(_ call: CAPPluginCall) {
83+
guard self.rejectIfPurchasesNotConfigured(call) else { return }
84+
CommonFunctionality.invalidateVirtualCurrenciesCache()
85+
call.resolve()
86+
}
87+
88+
@objc func getCachedVirtualCurrencies(_ call: CAPPluginCall) {
89+
guard self.rejectIfPurchasesNotConfigured(call) else { return }
90+
call.resolve([
91+
"cachedVirtualCurrencies": CommonFunctionality.getCachedVirtualCurrencies()
92+
])
93+
}
94+
7295
@objc func parseAsWebPurchaseRedemption(_ call: CAPPluginCall) {
7396
guard let urlString = call.getOrRejectString("urlString") else { return }
7497
let result: [String: Any?]

src/definitions.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {
1919
PurchasesStoreProduct,
2020
PurchasesStoreProductDiscount,
2121
PurchasesStoreTransaction,
22+
PurchasesVirtualCurrencies,
2223
PurchasesWinBackOffer,
2324
REFUND_REQUEST_STATUS,
2425
Storefront,
@@ -200,6 +201,33 @@ export interface PurchasesPlugin {
200201
*/
201202
configure(configuration: PurchasesConfiguration): Promise<void>;
202203

204+
/**
205+
* Fetches the virtual currencies for the current subscriber.
206+
*
207+
* @returns {Promise<{ virtualCurrencies: PurchasesVirtualCurrencies }>} A promise of a {@link PurchasesVirtualCurrencies} object.
208+
* The promise will be rejected if configure has not been called yet or if an error occurs while getting the virtual currencies.
209+
*/
210+
getVirtualCurrencies(): Promise<{ virtualCurrencies: PurchasesVirtualCurrencies }>;
211+
212+
/**
213+
* Invalidates the cache for virtual currencies.
214+
*
215+
* This is useful for cases where a virtual currency's balance might have been updated
216+
* outside of the app, like if you decreased a user's balance from the user spending a virtual currency,
217+
* or if you increased the balance from your backend using the server APIs.
218+
*/
219+
invalidateVirtualCurrenciesCache(): Promise<void>;
220+
221+
/**
222+
* The currently cached {@link PurchasesVirtualCurrencies} if one is available.
223+
* This value will remain null until virtual currencies have been fetched at
224+
* least once with {@link getVirtualCurrencies} or an equivalent function.
225+
*
226+
* @returns {Promise<{ cachedVirtualCurrencies: PurchasesVirtualCurrencies | null }>} The currently cached virtual currencies for the current subscriber.
227+
* The promise will be rejected if configure has not been called yet or there's an error.
228+
*/
229+
getCachedVirtualCurrencies(): Promise<{ cachedVirtualCurrencies: PurchasesVirtualCurrencies | null }>;
230+
203231
/**
204232
* Parses the given URL string into a [WebPurchaseRedemption] object that can be used to redeem web purchases.
205233
* @param options Set the urlString used to open the App.

src/web.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
PurchasesPromotionalOffer,
1919
PurchasesStoreProduct,
2020
PurchasesStoreTransaction,
21+
PurchasesVirtualCurrencies,
2122
PurchasesWinBackOffer,
2223
ShouldPurchasePromoProductListener,
2324
Storefront,
@@ -350,6 +351,22 @@ export class PurchasesWeb extends WebPlugin implements PurchasesPlugin {
350351
return this.mockReturningFunctionIfEnabled('isConfigured', mockResult);
351352
}
352353

354+
getVirtualCurrencies(): Promise<{ virtualCurrencies: PurchasesVirtualCurrencies }> {
355+
return this.mockReturningFunctionIfEnabled('getVirtualCurrencies', {
356+
virtualCurrencies: this.mockEmptyVirtualCurrencies,
357+
});
358+
}
359+
360+
invalidateVirtualCurrenciesCache(): Promise<void> {
361+
return this.mockNonReturningFunctionIfEnabled('invalidateVirtualCurrenciesCache');
362+
}
363+
364+
getCachedVirtualCurrencies(): Promise<{ cachedVirtualCurrencies: PurchasesVirtualCurrencies | null }> {
365+
return this.mockReturningFunctionIfEnabled('getCachedVirtualCurrencies', {
366+
cachedVirtualCurrencies: this.mockEmptyVirtualCurrencies,
367+
});
368+
}
369+
353370
// Mock helpers
354371

355372
private mockEmptyCustomerInfo: CustomerInfo = {
@@ -373,6 +390,10 @@ export class PurchasesWeb extends WebPlugin implements PurchasesPlugin {
373390
subscriptionsByProductIdentifier: {},
374391
};
375392

393+
private mockEmptyVirtualCurrencies: PurchasesVirtualCurrencies = {
394+
all: {},
395+
};
396+
376397
private mockTransaction(productIdentifier: string): PurchasesStoreTransaction {
377398
return {
378399
productIdentifier: productIdentifier,

0 commit comments

Comments
 (0)