Skip to content

Commit 7a6e56c

Browse files
committed
fix(revenuecat): align tools and block with REST v1 API spec
1 parent 76d602f commit 7a6e56c

14 files changed

Lines changed: 283 additions & 80 deletions

apps/docs/content/docs/en/tools/revenuecat.mdx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,10 @@ Record a purchase (receipt) for a subscriber via the REST API
119119
| `productId` | string | Yes | The product identifier for the purchase |
120120
| `price` | number | No | The price of the product in the currency specified |
121121
| `currency` | string | No | ISO 4217 currency code \(e.g., USD, EUR\) |
122-
| `isRestore` | boolean | No | Whether this is a restore of a previous purchase |
123-
| `platform` | string | No | Platform of the purchase \(ios, android, amazon, macos, stripe\). Required for Stripe and Paddle purchases. |
122+
| `isRestore` | boolean | No | Whether this is a restore of a previous purchase \(deprecated by RevenueCat\) |
123+
| `presentedOfferingIdentifier` | string | No | Identifier of the offering that was presented to the user when they made this purchase. Used by RevenueCat for offering-level analytics. |
124+
| `paymentMode` | string | No | Payment mode for the purchase. One of: pay_as_you_go, pay_up_front, free_trial. Only applies to introductory pricing periods. |
125+
| `platform` | string | Yes | Platform of the purchase. One of: ios, android, amazon, macos, uikitformac, stripe, roku, paddle. Sent as the X-Platform header \(required by RevenueCat\). |
124126

125127
#### Output
126128

@@ -170,7 +172,8 @@ Grant a promotional entitlement to a subscriber
170172
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
171173
| `appUserId` | string | Yes | The app user ID of the subscriber |
172174
| `entitlementIdentifier` | string | Yes | The entitlement identifier to grant |
173-
| `duration` | string | Yes | Duration of the entitlement \(daily, three_day, weekly, monthly, two_month, three_month, six_month, yearly, lifetime\) |
175+
| `duration` | string | No | Duration of the entitlement. Provide either duration or endTimeMs. One of: daily, three_day, weekly, two_week, monthly, two_month, three_month, six_month, yearly, lifetime |
176+
| `endTimeMs` | number | No | Absolute end time in milliseconds since Unix epoch. Use instead of duration to grant the entitlement until a specific timestamp. |
174177
| `startTimeMs` | number | No | Optional start time in milliseconds since Unix epoch. Set to a past time to achieve custom durations shorter than daily. |
175178

176179
#### Output
@@ -296,7 +299,7 @@ Update custom subscriber attributes (e.g., $email, $displayName, or custom key-v
296299
| --------- | ---- | -------- | ----------- |
297300
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
298301
| `appUserId` | string | Yes | The app user ID of the subscriber |
299-
| `attributes` | json | Yes | JSON object of attributes to set. Each key maps to an object with a "value" field. Example: \{"$email": \{"value": "user@example.com"\}, "$displayName": \{"value": "John"\}\} |
302+
| `attributes` | json | Yes | JSON object of attributes to set. Each key maps to an object with "value" \(string; null or empty deletes the attribute\) and "updated_at_ms" \(Unix epoch ms used for conflict resolution — required\). Example: \{"$email": \{"value": "user@example.com", "updated_at_ms": 1709195668093\}\} |
300303

301304
#### Output
302305

@@ -316,7 +319,8 @@ Defer a Google Play subscription by extending its billing date by a number of da
316319
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
317320
| `appUserId` | string | Yes | The app user ID of the subscriber |
318321
| `productId` | string | Yes | The Google Play product identifier of the subscription to defer \(use the part before the colon for products set up after Feb 2023\) |
319-
| `extendByDays` | number | Yes | Number of days to extend the subscription by \(1-365\) |
322+
| `extendByDays` | number | No | Number of days to extend the subscription by \(1-365\). Provide either extendByDays or expiryTimeMs. |
323+
| `expiryTimeMs` | number | No | Absolute new expiry time in milliseconds since Unix epoch. Use instead of extendByDays to set an exact expiry. |
320324

321325
#### Output
322326

@@ -357,15 +361,15 @@ Defer a Google Play subscription by extending its billing date by a number of da
357361

358362
### `revenuecat_refund_google_subscription`
359363

360-
Refund and optionally revoke a Google Play subscription (Google Play only)
364+
Refund a specific store transaction by its store transaction identifier and revoke access (subscription or non-subscription, last 365 days)
361365

362366
#### Input
363367

364368
| Parameter | Type | Required | Description |
365369
| --------- | ---- | -------- | ----------- |
366370
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
367371
| `appUserId` | string | Yes | The app user ID of the subscriber |
368-
| `productId` | string | Yes | The Google Play product identifier of the subscription to refund |
372+
| `storeTransactionId` | string | Yes | The store transaction identifier of the purchase to refund \(e.g., GPA.3309-9122-6177-45730 for Google Play\) |
369373

370374
#### Output
371375

apps/sim/app/(landing)/integrations/data/integrations.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10748,7 +10748,7 @@
1074810748
},
1074910749
{
1075010750
"name": "Refund Google Subscription",
10751-
"description": "Refund and optionally revoke a Google Play subscription (Google Play only)"
10751+
"description": "Refund a specific store transaction by its store transaction identifier and revoke access (subscription or non-subscription, last 365 days)"
1075210752
},
1075310753
{
1075410754
"name": "Revoke Google Subscription",

apps/sim/blocks/blocks/revenuecat.ts

Lines changed: 130 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const RevenueCatBlock: BlockConfig<RevenueCatResponse> = {
7272
{ label: 'Daily', id: 'daily' },
7373
{ label: '3 Days', id: 'three_day' },
7474
{ label: 'Weekly', id: 'weekly' },
75+
{ label: '2 Weeks', id: 'two_week' },
7576
{ label: 'Monthly', id: 'monthly' },
7677
{ label: '2 Months', id: 'two_month' },
7778
{ label: '3 Months', id: 'three_month' },
@@ -85,6 +86,28 @@ export const RevenueCatBlock: BlockConfig<RevenueCatResponse> = {
8586
value: 'grant_entitlement',
8687
},
8788
},
89+
{
90+
id: 'endTimeMs',
91+
title: 'End Time (ms)',
92+
type: 'short-input',
93+
placeholder: 'Optional absolute end time in ms since epoch',
94+
condition: {
95+
field: 'operation',
96+
value: 'grant_entitlement',
97+
},
98+
mode: 'advanced',
99+
wandConfig: {
100+
enabled: true,
101+
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
102+
The timestamp should represent the absolute end time for the entitlement.
103+
Examples:
104+
- "in 7 days" -> current time plus 604800000 milliseconds
105+
- "next month" -> current time plus 2592000000 milliseconds
106+
- "end of 2026" -> 1798761600000
107+
108+
Return ONLY the numeric timestamp, no text.`,
109+
},
110+
},
88111
{
89112
id: 'startTimeMs',
90113
title: 'Start Time (ms)',
@@ -129,21 +152,25 @@ Return ONLY the numeric timestamp, no text.`,
129152
placeholder: 'Product identifier',
130153
condition: {
131154
field: 'operation',
132-
value: [
133-
'create_purchase',
134-
'defer_google_subscription',
135-
'refund_google_subscription',
136-
'revoke_google_subscription',
137-
],
155+
value: ['create_purchase', 'defer_google_subscription', 'revoke_google_subscription'],
156+
},
157+
required: {
158+
field: 'operation',
159+
value: ['create_purchase', 'defer_google_subscription', 'revoke_google_subscription'],
160+
},
161+
},
162+
{
163+
id: 'storeTransactionId',
164+
title: 'Store Transaction ID',
165+
type: 'short-input',
166+
placeholder: 'e.g., GPA.3309-9122-6177-45730',
167+
condition: {
168+
field: 'operation',
169+
value: 'refund_google_subscription',
138170
},
139171
required: {
140172
field: 'operation',
141-
value: [
142-
'create_purchase',
143-
'defer_google_subscription',
144-
'refund_google_subscription',
145-
'revoke_google_subscription',
146-
],
173+
value: 'refund_google_subscription',
147174
},
148175
},
149176
{
@@ -168,6 +195,32 @@ Return ONLY the numeric timestamp, no text.`,
168195
},
169196
mode: 'advanced',
170197
},
198+
{
199+
id: 'presentedOfferingIdentifier',
200+
title: 'Presented Offering ID',
201+
type: 'short-input',
202+
placeholder: 'Offering identifier shown to the user',
203+
condition: {
204+
field: 'operation',
205+
value: 'create_purchase',
206+
},
207+
mode: 'advanced',
208+
},
209+
{
210+
id: 'paymentMode',
211+
title: 'Payment Mode',
212+
type: 'dropdown',
213+
options: [
214+
{ label: 'Pay As You Go', id: 'pay_as_you_go' },
215+
{ label: 'Pay Up Front', id: 'pay_up_front' },
216+
{ label: 'Free Trial', id: 'free_trial' },
217+
],
218+
condition: {
219+
field: 'operation',
220+
value: 'create_purchase',
221+
},
222+
mode: 'advanced',
223+
},
171224
{
172225
id: 'isRestore',
173226
title: 'Is Restore',
@@ -192,13 +245,19 @@ Return ONLY the numeric timestamp, no text.`,
192245
{ label: 'Android', id: 'android' },
193246
{ label: 'Amazon', id: 'amazon' },
194247
{ label: 'macOS', id: 'macos' },
248+
{ label: 'UIKit for Mac', id: 'uikitformac' },
195249
{ label: 'Stripe', id: 'stripe' },
250+
{ label: 'Roku', id: 'roku' },
251+
{ label: 'Paddle', id: 'paddle' },
196252
],
197253
condition: {
198254
field: 'operation',
199255
value: 'create_purchase',
200256
},
201-
mode: 'advanced',
257+
required: {
258+
field: 'operation',
259+
value: 'create_purchase',
260+
},
202261
},
203262
{
204263
id: 'attributes',
@@ -238,10 +297,24 @@ Return ONLY valid JSON.`,
238297
field: 'operation',
239298
value: 'defer_google_subscription',
240299
},
241-
required: {
300+
},
301+
{
302+
id: 'expiryTimeMs',
303+
title: 'Expiry Time (ms)',
304+
type: 'short-input',
305+
placeholder: 'Absolute new expiry time in ms since epoch',
306+
condition: {
242307
field: 'operation',
243308
value: 'defer_google_subscription',
244309
},
310+
mode: 'advanced',
311+
wandConfig: {
312+
enabled: true,
313+
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
314+
The timestamp should represent the new absolute expiry time of the subscription.
315+
316+
Return ONLY the numeric timestamp, no text.`,
317+
},
245318
},
246319
{
247320
id: 'platform',
@@ -251,13 +324,15 @@ Return ONLY valid JSON.`,
251324
{ label: 'iOS', id: 'ios' },
252325
{ label: 'Android', id: 'android' },
253326
{ label: 'Amazon', id: 'amazon' },
254-
{ label: 'macOS', id: 'macos' },
255327
{ label: 'Stripe', id: 'stripe' },
328+
{ label: 'Roku', id: 'roku' },
329+
{ label: 'Paddle', id: 'paddle' },
256330
],
257331
condition: {
258332
field: 'operation',
259333
value: 'list_offerings',
260334
},
335+
mode: 'advanced',
261336
},
262337
],
263338
tools: {
@@ -274,23 +349,32 @@ Return ONLY valid JSON.`,
274349
'revenuecat_revoke_google_subscription',
275350
],
276351
config: {
277-
tool: (params) => {
352+
tool: (params) => `revenuecat_${params.operation}`,
353+
params: (params) => {
354+
const next: Record<string, unknown> = { ...params }
278355
if (params.purchasePlatform && params.operation === 'create_purchase') {
279-
params.platform = params.purchasePlatform
356+
next.platform = params.purchasePlatform
280357
}
281-
if (params.isRestore !== undefined) {
282-
params.isRestore = params.isRestore === 'true'
358+
next.purchasePlatform = undefined
359+
if (params.isRestore !== undefined && params.isRestore !== '') {
360+
next.isRestore = params.isRestore === true || params.isRestore === 'true'
283361
}
284362
if (params.price !== undefined && params.price !== '') {
285-
params.price = Number(params.price)
363+
next.price = Number(params.price)
286364
}
287365
if (params.extendByDays !== undefined && params.extendByDays !== '') {
288-
params.extendByDays = Number(params.extendByDays)
366+
next.extendByDays = Number(params.extendByDays)
289367
}
290368
if (params.startTimeMs !== undefined && params.startTimeMs !== '') {
291-
params.startTimeMs = Number(params.startTimeMs)
369+
next.startTimeMs = Number(params.startTimeMs)
370+
}
371+
if (params.endTimeMs !== undefined && params.endTimeMs !== '') {
372+
next.endTimeMs = Number(params.endTimeMs)
292373
}
293-
return `revenuecat_${params.operation}`
374+
if (params.expiryTimeMs !== undefined && params.expiryTimeMs !== '') {
375+
next.expiryTimeMs = Number(params.expiryTimeMs)
376+
}
377+
return next
294378
},
295379
},
296380
},
@@ -303,25 +387,43 @@ Return ONLY valid JSON.`,
303387
startTimeMs: { type: 'number', description: 'Custom start time in ms since epoch' },
304388
fetchToken: { type: 'string', description: 'Store receipt or purchase token' },
305389
productId: { type: 'string', description: 'Product identifier' },
390+
storeTransactionId: { type: 'string', description: 'Store transaction identifier' },
306391
price: { type: 'number', description: 'Product price' },
307392
currency: { type: 'string', description: 'ISO 4217 currency code' },
308393
isRestore: { type: 'boolean', description: 'Whether this is a restore purchase' },
309-
purchasePlatform: { type: 'string', description: 'Platform for the purchase' },
394+
presentedOfferingIdentifier: {
395+
type: 'string',
396+
description: 'Identifier of the offering presented to the user',
397+
},
398+
paymentMode: {
399+
type: 'string',
400+
description: 'Payment mode (pay_as_you_go, pay_up_front, free_trial)',
401+
},
310402
attributes: { type: 'string', description: 'JSON object of subscriber attributes' },
311403
extendByDays: { type: 'number', description: 'Number of days to extend (1-365)' },
312-
platform: { type: 'string', description: 'Platform filter for offerings' },
404+
expiryTimeMs: { type: 'number', description: 'Absolute new expiry time in ms since epoch' },
405+
endTimeMs: {
406+
type: 'number',
407+
description: 'Absolute end time for entitlement in ms since epoch',
408+
},
409+
platform: { type: 'string', description: 'Platform (X-Platform header)' },
313410
},
314411
outputs: {
315412
subscriber: {
316413
type: 'json',
317-
description: 'Subscriber object with subscriptions and entitlements',
414+
description:
415+
'Subscriber object (first_seen, original_app_user_id, original_purchase_date, management_url, subscriptions, entitlements, non_subscriptions)',
318416
},
319417
offerings: {
320418
type: 'json',
321-
description: 'Array of offerings with packages',
419+
description: 'Array of offerings, each with identifier, description, and packages[]',
322420
},
323421
current_offering_id: { type: 'string', description: 'Current offering identifier' },
324-
metadata: { type: 'json', description: 'Operation metadata' },
422+
metadata: {
423+
type: 'json',
424+
description:
425+
'Operation metadata. For get_customer: app_user_id, first_seen, active_entitlements, active_subscriptions. For list_offerings: count, current_offering_id.',
426+
},
325427
deleted: { type: 'boolean', description: 'Whether the subscriber was deleted' },
326428
app_user_id: { type: 'string', description: 'The app user ID' },
327429
updated: { type: 'boolean', description: 'Whether the attributes were updated' },

apps/sim/tools/revenuecat/create_purchase.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { CreatePurchaseParams, CreatePurchaseResponse } from '@/tools/revenuecat/types'
2-
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
2+
import { SUBSCRIBER_OUTPUT, throwIfRevenueCatError } from '@/tools/revenuecat/types'
33
import type { ToolConfig } from '@/tools/types'
44

55
export const revenuecatCreatePurchaseTool: ToolConfig<
@@ -53,14 +53,28 @@ export const revenuecatCreatePurchaseTool: ToolConfig<
5353
type: 'boolean',
5454
required: false,
5555
visibility: 'user-or-llm',
56-
description: 'Whether this is a restore of a previous purchase',
56+
description: 'Whether this is a restore of a previous purchase (deprecated by RevenueCat)',
5757
},
58-
platform: {
58+
presentedOfferingIdentifier: {
5959
type: 'string',
6060
required: false,
6161
visibility: 'user-or-llm',
6262
description:
63-
'Platform of the purchase (ios, android, amazon, macos, stripe). Required for Stripe and Paddle purchases.',
63+
'Identifier of the offering that was presented to the user when they made this purchase. Used by RevenueCat for offering-level analytics.',
64+
},
65+
paymentMode: {
66+
type: 'string',
67+
required: false,
68+
visibility: 'user-or-llm',
69+
description:
70+
'Payment mode for the purchase. One of: pay_as_you_go, pay_up_front, free_trial. Only applies to introductory pricing periods.',
71+
},
72+
platform: {
73+
type: 'string',
74+
required: true,
75+
visibility: 'user-or-llm',
76+
description:
77+
'Platform of the purchase. One of: ios, android, amazon, macos, uikitformac, stripe, roku, paddle. Sent as the X-Platform header (required by RevenueCat).',
6478
},
6579
},
6680

@@ -86,11 +100,16 @@ export const revenuecatCreatePurchaseTool: ToolConfig<
86100
if (params.price !== undefined) body.price = params.price
87101
if (params.currency) body.currency = params.currency
88102
if (params.isRestore !== undefined) body.is_restore = params.isRestore
103+
if (params.presentedOfferingIdentifier) {
104+
body.presented_offering_identifier = params.presentedOfferingIdentifier
105+
}
106+
if (params.paymentMode) body.payment_mode = params.paymentMode
89107
return body
90108
},
91109
},
92110

93111
transformResponse: async (response) => {
112+
await throwIfRevenueCatError(response)
94113
const data = await response.json()
95114
const subscriber = data.subscriber ?? {}
96115

0 commit comments

Comments
 (0)