mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(revenuecat): align tools and block with REST v1 API spec
- Validated all 10 tools and the block against context7 REST v1 docs
- Unwrap {value:{subscriber}} envelope across post-receipts, attributes,
entitlements, and Google subscription endpoints
- Trim entitlement output to documented fields (expires_date,
grace_period_expires_date, product_identifier, purchase_date)
- Add subscriber output fields: last_seen, original_application_version,
other_purchases, subscriber_attributes
- create_purchase: productId optional (Google-only required); add
introductoryPrice, attributes, updated_at_ms; surface customer + subscriber;
X-Platform required; presentedOfferingIdentifier and paymentMode
- update_subscriber_attributes: read response and surface subscriber;
note required updated_at_ms
- defer_google_subscription: enforce XOR(extendByDays, expiryTimeMs) and
1-365 range; expiryTimeMs as one-of alternative
- grant_entitlement: duration optional, added endTimeMs (one-of)
- refund_google_subscription: corrected endpoint to
/transactions/{storeTransactionId}/refund
- delete_customer: read 'deleted' field (was 'was_deleted')
- list_offerings: corrected X-Platform values
- get_customer: count active subscriptions by expiry/refund
- Added shared throwIfRevenueCatError helper for {code, message} envelope
- Moved type coercions from tools.config.tool to tools.config.params to
preserve dynamic refs
This commit is contained in:
@@ -51,8 +51,10 @@ Retrieve subscriber information by app user ID
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The subscriber object with subscriptions and entitlements |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -71,16 +73,13 @@ Retrieve subscriber information by app user ID
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
| `metadata` | object | Subscriber summary metadata |
|
||||
| ↳ `app_user_id` | string | The app user ID |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when the subscriber was first seen |
|
||||
@@ -115,21 +114,29 @@ Record a purchase (receipt) for a subscriber via the REST API
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | RevenueCat API key \(public or secret\) |
|
||||
| `appUserId` | string | Yes | The app user ID of the subscriber |
|
||||
| `fetchToken` | string | Yes | The receipt token or purchase token from the store \(App Store receipt, Google Play purchase token, or Stripe subscription ID\) |
|
||||
| `productId` | string | Yes | The product identifier for the purchase |
|
||||
| `price` | number | No | The price of the product in the currency specified |
|
||||
| `currency` | string | No | ISO 4217 currency code \(e.g., USD, EUR\) |
|
||||
| `isRestore` | boolean | No | Whether this is a restore of a previous purchase |
|
||||
| `platform` | string | No | Platform of the purchase \(ios, android, amazon, macos, stripe\). Required for Stripe and Paddle purchases. |
|
||||
| `fetchToken` | string | Yes | For iOS, the base64-encoded receipt \(or JWSTransaction for StoreKit2\); for Android the purchase token; for Amazon the receipt; for Stripe the subscription ID or Checkout Session ID; for Roku the transaction ID; for Paddle the subscription ID or transaction ID |
|
||||
| `productId` | string | No | Apple, Google, Amazon, Roku, or Paddle product identifier or SKU. Required for Google. |
|
||||
| `price` | number | No | Price of the product. Required if you provide a currency. |
|
||||
| `currency` | string | No | ISO 4217 currency code \(e.g., USD, EUR\). Required if you provide a price. |
|
||||
| `isRestore` | boolean | No | Deprecated. Triggers configured restore behavior for shared fetch tokens. |
|
||||
| `presentedOfferingIdentifier` | string | No | Identifier of the offering presented to the customer at the time of purchase. Attached to new transactions in this fetch token and exposed in ETL exports and webhooks. |
|
||||
| `paymentMode` | string | No | Payment mode for the introductory period. One of: pay_as_you_go, pay_up_front, free_trial. Defaults to free_trial when an introductory period is detected and no value is provided. |
|
||||
| `introductoryPrice` | number | No | Introductory price paid \(if any\). |
|
||||
| `attributes` | json | No | JSON object of subscriber attributes to set alongside the purchase. Each key maps to \{"value": string, "updated_at_ms": number\}. |
|
||||
| `updatedAtMs` | number | No | UNIX epoch in milliseconds used to resolve attribute conflicts at the request level. |
|
||||
| `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\). |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | Customer object returned at the top level of POST /v1/receipts \(first_seen, last_seen, original_app_user_id, original_application_version, original_sdk_version, management_url, entitlements, original_purchase_date, request_date\). Null when the response uses the `value`-wrapped envelope. |
|
||||
| `subscriber` | object | The updated subscriber object after recording the purchase |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -148,16 +155,13 @@ Record a purchase (receipt) for a subscriber via the REST API
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_grant_entitlement`
|
||||
|
||||
@@ -170,7 +174,8 @@ Grant a promotional entitlement to a subscriber
|
||||
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
|
||||
| `appUserId` | string | Yes | The app user ID of the subscriber |
|
||||
| `entitlementIdentifier` | string | Yes | The entitlement identifier to grant |
|
||||
| `duration` | string | Yes | Duration of the entitlement \(daily, three_day, weekly, monthly, two_month, three_month, six_month, yearly, lifetime\) |
|
||||
| `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 |
|
||||
| `endTimeMs` | number | No | Absolute end time in milliseconds since Unix epoch. Use instead of duration to grant the entitlement until a specific timestamp. |
|
||||
| `startTimeMs` | number | No | Optional start time in milliseconds since Unix epoch. Set to a past time to achieve custom durations shorter than daily. |
|
||||
|
||||
#### Output
|
||||
@@ -179,8 +184,10 @@ Grant a promotional entitlement to a subscriber
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The updated subscriber object after granting the entitlement |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -199,16 +206,13 @@ Grant a promotional entitlement to a subscriber
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_revoke_entitlement`
|
||||
|
||||
@@ -228,8 +232,10 @@ Revoke all promotional entitlements for a specific entitlement identifier
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The updated subscriber object after revoking the entitlement |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -248,16 +254,13 @@ Revoke all promotional entitlements for a specific entitlement identifier
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_list_offerings`
|
||||
|
||||
@@ -296,7 +299,7 @@ Update custom subscriber attributes (e.g., $email, $displayName, or custom key-v
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
|
||||
| `appUserId` | string | Yes | The app user ID of the subscriber |
|
||||
| `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"\}\} |
|
||||
| `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\}\} |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -304,6 +307,37 @@ Update custom subscriber attributes (e.g., $email, $displayName, or custom key-v
|
||||
| --------- | ---- | ----------- |
|
||||
| `updated` | boolean | Whether the subscriber attributes were successfully updated |
|
||||
| `app_user_id` | string | The app user ID of the updated subscriber |
|
||||
| `subscriber` | object | The updated subscriber object after applying the attribute changes |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
| ↳ `original_transaction_id` | string | Original transaction identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 purchase date |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
|
||||
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
|
||||
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
|
||||
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
|
||||
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_defer_google_subscription`
|
||||
|
||||
@@ -316,7 +350,8 @@ Defer a Google Play subscription by extending its billing date by a number of da
|
||||
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
|
||||
| `appUserId` | string | Yes | The app user ID of the subscriber |
|
||||
| `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\) |
|
||||
| `extendByDays` | number | Yes | Number of days to extend the subscription by \(1-365\) |
|
||||
| `extendByDays` | number | No | Number of days to extend the subscription by \(1-365\). Provide either extendByDays or expiryTimeMs. |
|
||||
| `expiryTimeMs` | number | No | Absolute new expiry time in milliseconds since Unix epoch. Use instead of extendByDays to set an exact expiry. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -324,8 +359,10 @@ Defer a Google Play subscription by extending its billing date by a number of da
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The updated subscriber object after deferring the Google subscription |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -344,20 +381,17 @@ Defer a Google Play subscription by extending its billing date by a number of da
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_refund_google_subscription`
|
||||
|
||||
Refund and optionally revoke a Google Play subscription (Google Play only)
|
||||
Refund a specific store transaction by its store transaction identifier and revoke access (subscription or non-subscription, last 365 days)
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -365,7 +399,7 @@ Refund and optionally revoke a Google Play subscription (Google Play only)
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
|
||||
| `appUserId` | string | Yes | The app user ID of the subscriber |
|
||||
| `productId` | string | Yes | The Google Play product identifier of the subscription to refund |
|
||||
| `storeTransactionId` | string | Yes | The store transaction identifier of the purchase to refund \(e.g., GPA.3309-9122-6177-45730 for Google Play\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -373,8 +407,10 @@ Refund and optionally revoke a Google Play subscription (Google Play only)
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The updated subscriber object after refunding the Google subscription |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -393,16 +429,13 @@ Refund and optionally revoke a Google Play subscription (Google Play only)
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
### `revenuecat_revoke_google_subscription`
|
||||
|
||||
@@ -422,8 +455,10 @@ Immediately revoke access to a Google Play subscription and issue a refund (Goog
|
||||
| --------- | ---- | ----------- |
|
||||
| `subscriber` | object | The updated subscriber object after revoking the Google subscription |
|
||||
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
|
||||
| ↳ `last_seen` | string | ISO 8601 date when subscriber was last seen |
|
||||
| ↳ `original_app_user_id` | string | Original app user ID |
|
||||
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
|
||||
| ↳ `original_application_version` | string | iOS only. First App Store version of your app the customer installed |
|
||||
| ↳ `original_purchase_date` | string | iOS only. Date the app was first purchased/downloaded |
|
||||
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
|
||||
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
|
||||
| ↳ `store_transaction_id` | string | Store transaction identifier |
|
||||
@@ -442,15 +477,12 @@ Immediately revoke access to a Google Play subscription and issue a refund (Goog
|
||||
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
|
||||
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
|
||||
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
|
||||
| ↳ `grant_date` | string | ISO 8601 grant date |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `is_active` | boolean | Whether the entitlement is active |
|
||||
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
|
||||
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `store` | string | Store the entitlement was granted from |
|
||||
| ↳ `expires_date` | string | ISO 8601 expiration date \(null for non-expiring entitlements\) |
|
||||
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
|
||||
| ↳ `product_identifier` | string | Product identifier |
|
||||
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
|
||||
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
|
||||
| ↳ `other_purchases` | object | Other purchases attached to the subscriber |
|
||||
| ↳ `subscriber_attributes` | object | Custom attributes set on the subscriber. Only returned when using a secret API key |
|
||||
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ export const RevenueCatBlock: BlockConfig<RevenueCatResponse> = {
|
||||
{ label: 'Daily', id: 'daily' },
|
||||
{ label: '3 Days', id: 'three_day' },
|
||||
{ label: 'Weekly', id: 'weekly' },
|
||||
{ label: '2 Weeks', id: 'two_week' },
|
||||
{ label: 'Monthly', id: 'monthly' },
|
||||
{ label: '2 Months', id: 'two_month' },
|
||||
{ label: '3 Months', id: 'three_month' },
|
||||
@@ -85,6 +86,28 @@ export const RevenueCatBlock: BlockConfig<RevenueCatResponse> = {
|
||||
value: 'grant_entitlement',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'endTimeMs',
|
||||
title: 'End Time (ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional absolute end time in ms since epoch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'grant_entitlement',
|
||||
},
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
|
||||
The timestamp should represent the absolute end time for the entitlement.
|
||||
Examples:
|
||||
- "in 7 days" -> current time plus 604800000 milliseconds
|
||||
- "next month" -> current time plus 2592000000 milliseconds
|
||||
- "end of 2026" -> 1798761600000
|
||||
|
||||
Return ONLY the numeric timestamp, no text.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'startTimeMs',
|
||||
title: 'Start Time (ms)',
|
||||
@@ -124,9 +147,10 @@ Return ONLY the numeric timestamp, no text.`,
|
||||
},
|
||||
{
|
||||
id: 'productId',
|
||||
title: 'Product ID',
|
||||
title: 'Product ID / Store Transaction ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Product identifier',
|
||||
placeholder:
|
||||
'Product ID, or store transaction ID for refunds (e.g., GPA.3309-9122-6177-45730)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
@@ -139,7 +163,6 @@ Return ONLY the numeric timestamp, no text.`,
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'create_purchase',
|
||||
'defer_google_subscription',
|
||||
'refund_google_subscription',
|
||||
'revoke_google_subscription',
|
||||
@@ -168,6 +191,61 @@ Return ONLY the numeric timestamp, no text.`,
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'presentedOfferingIdentifier',
|
||||
title: 'Presented Offering ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Offering identifier shown to the user',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'paymentMode',
|
||||
title: 'Payment Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Pay As You Go', id: 'pay_as_you_go' },
|
||||
{ label: 'Pay Up Front', id: 'pay_up_front' },
|
||||
{ label: 'Free Trial', id: 'free_trial' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'introductoryPrice',
|
||||
title: 'Introductory Price',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 0.99',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'updatedAtMs',
|
||||
title: 'Updated At (ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Unix epoch ms used to resolve attribute conflicts',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
|
||||
Used by RevenueCat to resolve attribute conflicts on a posted purchase.
|
||||
|
||||
Return ONLY the numeric timestamp, no text.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'isRestore',
|
||||
title: 'Is Restore',
|
||||
@@ -192,13 +270,19 @@ Return ONLY the numeric timestamp, no text.`,
|
||||
{ label: 'Android', id: 'android' },
|
||||
{ label: 'Amazon', id: 'amazon' },
|
||||
{ label: 'macOS', id: 'macos' },
|
||||
{ label: 'UIKit for Mac', id: 'uikitformac' },
|
||||
{ label: 'Stripe', id: 'stripe' },
|
||||
{ label: 'Roku', id: 'roku' },
|
||||
{ label: 'Paddle', id: 'paddle' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
mode: 'advanced',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: 'create_purchase',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'attributes',
|
||||
@@ -207,7 +291,7 @@ Return ONLY the numeric timestamp, no text.`,
|
||||
placeholder: '{"$email": {"value": "user@example.com"}}',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update_subscriber_attributes',
|
||||
value: ['update_subscriber_attributes', 'create_purchase'],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
@@ -238,10 +322,24 @@ Return ONLY valid JSON.`,
|
||||
field: 'operation',
|
||||
value: 'defer_google_subscription',
|
||||
},
|
||||
required: {
|
||||
},
|
||||
{
|
||||
id: 'expiryTimeMs',
|
||||
title: 'Expiry Time (ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Absolute new expiry time in ms since epoch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'defer_google_subscription',
|
||||
},
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
|
||||
The timestamp should represent the new absolute expiry time of the subscription.
|
||||
|
||||
Return ONLY the numeric timestamp, no text.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'platform',
|
||||
@@ -251,13 +349,15 @@ Return ONLY valid JSON.`,
|
||||
{ label: 'iOS', id: 'ios' },
|
||||
{ label: 'Android', id: 'android' },
|
||||
{ label: 'Amazon', id: 'amazon' },
|
||||
{ label: 'macOS', id: 'macos' },
|
||||
{ label: 'Stripe', id: 'stripe' },
|
||||
{ label: 'Roku', id: 'roku' },
|
||||
{ label: 'Paddle', id: 'paddle' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'list_offerings',
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
@@ -274,23 +374,42 @@ Return ONLY valid JSON.`,
|
||||
'revenuecat_revoke_google_subscription',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
tool: (params) => `revenuecat_${params.operation}`,
|
||||
params: (params) => {
|
||||
const next: Record<string, unknown> = { ...params }
|
||||
if (params.purchasePlatform && params.operation === 'create_purchase') {
|
||||
params.platform = params.purchasePlatform
|
||||
next.platform = params.purchasePlatform
|
||||
}
|
||||
if (params.isRestore !== undefined) {
|
||||
params.isRestore = params.isRestore === 'true'
|
||||
next.purchasePlatform = undefined
|
||||
if (params.productId && params.operation === 'refund_google_subscription') {
|
||||
next.storeTransactionId = params.productId
|
||||
next.productId = undefined
|
||||
}
|
||||
if (params.isRestore !== undefined && params.isRestore !== '') {
|
||||
next.isRestore = params.isRestore === true || params.isRestore === 'true'
|
||||
}
|
||||
if (params.price !== undefined && params.price !== '') {
|
||||
params.price = Number(params.price)
|
||||
next.price = Number(params.price)
|
||||
}
|
||||
if (params.extendByDays !== undefined && params.extendByDays !== '') {
|
||||
params.extendByDays = Number(params.extendByDays)
|
||||
next.extendByDays = Number(params.extendByDays)
|
||||
}
|
||||
if (params.startTimeMs !== undefined && params.startTimeMs !== '') {
|
||||
params.startTimeMs = Number(params.startTimeMs)
|
||||
next.startTimeMs = Number(params.startTimeMs)
|
||||
}
|
||||
return `revenuecat_${params.operation}`
|
||||
if (params.endTimeMs !== undefined && params.endTimeMs !== '') {
|
||||
next.endTimeMs = Number(params.endTimeMs)
|
||||
}
|
||||
if (params.expiryTimeMs !== undefined && params.expiryTimeMs !== '') {
|
||||
next.expiryTimeMs = Number(params.expiryTimeMs)
|
||||
}
|
||||
if (params.introductoryPrice !== undefined && params.introductoryPrice !== '') {
|
||||
next.introductoryPrice = Number(params.introductoryPrice)
|
||||
}
|
||||
if (params.updatedAtMs !== undefined && params.updatedAtMs !== '') {
|
||||
next.updatedAtMs = Number(params.updatedAtMs)
|
||||
}
|
||||
return next
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -302,28 +421,61 @@ Return ONLY valid JSON.`,
|
||||
duration: { type: 'string', description: 'Promotional entitlement duration' },
|
||||
startTimeMs: { type: 'number', description: 'Custom start time in ms since epoch' },
|
||||
fetchToken: { type: 'string', description: 'Store receipt or purchase token' },
|
||||
productId: { type: 'string', description: 'Product identifier' },
|
||||
productId: {
|
||||
type: 'string',
|
||||
description: 'Product identifier (or store transaction ID for refunds)',
|
||||
},
|
||||
price: { type: 'number', description: 'Product price' },
|
||||
currency: { type: 'string', description: 'ISO 4217 currency code' },
|
||||
isRestore: { type: 'boolean', description: 'Whether this is a restore purchase' },
|
||||
purchasePlatform: { type: 'string', description: 'Platform for the purchase' },
|
||||
attributes: { type: 'string', description: 'JSON object of subscriber attributes' },
|
||||
presentedOfferingIdentifier: {
|
||||
type: 'string',
|
||||
description: 'Identifier of the offering presented to the user',
|
||||
},
|
||||
paymentMode: {
|
||||
type: 'string',
|
||||
description: 'Payment mode (pay_as_you_go, pay_up_front, free_trial)',
|
||||
},
|
||||
attributes: {
|
||||
type: 'string',
|
||||
description:
|
||||
'JSON object of subscriber attributes (used by update_subscriber_attributes and create_purchase)',
|
||||
},
|
||||
introductoryPrice: { type: 'number', description: 'Introductory price for the purchase' },
|
||||
updatedAtMs: {
|
||||
type: 'number',
|
||||
description: 'Unix epoch ms used by RevenueCat to resolve attribute conflicts',
|
||||
},
|
||||
extendByDays: { type: 'number', description: 'Number of days to extend (1-365)' },
|
||||
platform: { type: 'string', description: 'Platform filter for offerings' },
|
||||
expiryTimeMs: { type: 'number', description: 'Absolute new expiry time in ms since epoch' },
|
||||
endTimeMs: {
|
||||
type: 'number',
|
||||
description: 'Absolute end time for entitlement in ms since epoch',
|
||||
},
|
||||
platform: { type: 'string', description: 'Platform (X-Platform header)' },
|
||||
},
|
||||
outputs: {
|
||||
subscriber: {
|
||||
type: 'json',
|
||||
description: 'Subscriber object with subscriptions and entitlements',
|
||||
description:
|
||||
'Subscriber object (first_seen, original_app_user_id, original_purchase_date, management_url, subscriptions, entitlements, non_subscriptions)',
|
||||
},
|
||||
offerings: {
|
||||
type: 'json',
|
||||
description: 'Array of offerings with packages',
|
||||
description: 'Array of offerings, each with identifier, description, and packages[]',
|
||||
},
|
||||
current_offering_id: { type: 'string', description: 'Current offering identifier' },
|
||||
metadata: { type: 'json', description: 'Operation metadata' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Operation metadata. For get_customer: app_user_id, first_seen, active_entitlements, active_subscriptions. For list_offerings: count, current_offering_id.',
|
||||
},
|
||||
deleted: { type: 'boolean', description: 'Whether the subscriber was deleted' },
|
||||
app_user_id: { type: 'string', description: 'The app user ID' },
|
||||
updated: { type: 'boolean', description: 'Whether the attributes were updated' },
|
||||
customer: {
|
||||
type: 'json',
|
||||
description: 'Customer object returned by create_purchase (when present in the response)',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { CreatePurchaseParams, CreatePurchaseResponse } from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractCustomer,
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatCreatePurchaseTool: ToolConfig<
|
||||
@@ -29,38 +35,73 @@ export const revenuecatCreatePurchaseTool: ToolConfig<
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The receipt token or purchase token from the store (App Store receipt, Google Play purchase token, or Stripe subscription ID)',
|
||||
'For iOS, the base64-encoded receipt (or JWSTransaction for StoreKit2); for Android the purchase token; for Amazon the receipt; for Stripe the subscription ID or Checkout Session ID; for Roku the transaction ID; for Paddle the subscription ID or transaction ID',
|
||||
},
|
||||
productId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The product identifier for the purchase',
|
||||
description:
|
||||
'Apple, Google, Amazon, Roku, or Paddle product identifier or SKU. Required for Google.',
|
||||
},
|
||||
price: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The price of the product in the currency specified',
|
||||
description: 'Price of the product. Required if you provide a currency.',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ISO 4217 currency code (e.g., USD, EUR)',
|
||||
description: 'ISO 4217 currency code (e.g., USD, EUR). Required if you provide a price.',
|
||||
},
|
||||
isRestore: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether this is a restore of a previous purchase',
|
||||
description: 'Deprecated. Triggers configured restore behavior for shared fetch tokens.',
|
||||
},
|
||||
platform: {
|
||||
presentedOfferingIdentifier: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Platform of the purchase (ios, android, amazon, macos, stripe). Required for Stripe and Paddle purchases.',
|
||||
'Identifier of the offering presented to the customer at the time of purchase. Attached to new transactions in this fetch token and exposed in ETL exports and webhooks.',
|
||||
},
|
||||
paymentMode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Payment mode for the introductory period. One of: pay_as_you_go, pay_up_front, free_trial. Defaults to free_trial when an introductory period is detected and no value is provided.',
|
||||
},
|
||||
introductoryPrice: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Introductory price paid (if any).',
|
||||
},
|
||||
attributes: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object of subscriber attributes to set alongside the purchase. Each key maps to {"value": string, "updated_at_ms": number}.',
|
||||
},
|
||||
updatedAtMs: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'UNIX epoch in milliseconds used to resolve attribute conflicts at the request level.',
|
||||
},
|
||||
platform: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Platform of the purchase. One of: ios, android, amazon, macos, uikitformac, stripe, roku, paddle. Sent as the X-Platform header (required by RevenueCat).',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -81,34 +122,46 @@ export const revenuecatCreatePurchaseTool: ToolConfig<
|
||||
const body: Record<string, unknown> = {
|
||||
app_user_id: params.appUserId,
|
||||
fetch_token: params.fetchToken,
|
||||
product_id: params.productId,
|
||||
}
|
||||
if (params.productId) body.product_id = params.productId
|
||||
if (params.price !== undefined) body.price = params.price
|
||||
if (params.currency) body.currency = params.currency
|
||||
if (params.isRestore !== undefined) body.is_restore = params.isRestore
|
||||
if (params.presentedOfferingIdentifier) {
|
||||
body.presented_offering_identifier = params.presentedOfferingIdentifier
|
||||
}
|
||||
if (params.paymentMode) body.payment_mode = params.paymentMode
|
||||
if (params.introductoryPrice !== undefined) {
|
||||
body.introductory_price = params.introductoryPrice
|
||||
}
|
||||
if (params.attributes !== undefined && params.attributes !== '') {
|
||||
body.attributes =
|
||||
typeof params.attributes === 'string' ? JSON.parse(params.attributes) : params.attributes
|
||||
}
|
||||
if (params.updatedAtMs !== undefined) body.updated_at_ms = params.updatedAtMs
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
non_subscriptions: subscriber.non_subscriptions ?? {},
|
||||
},
|
||||
customer: extractCustomer(data),
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
customer: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Customer object returned at the top level of POST /v1/receipts (first_seen, last_seen, original_app_user_id, original_application_version, original_sdk_version, management_url, entitlements, original_purchase_date, request_date). Null when the response uses the `value`-wrapped envelope.',
|
||||
optional: true,
|
||||
},
|
||||
subscriber: {
|
||||
...SUBSCRIBER_OUTPUT,
|
||||
description: 'The updated subscriber object after recording the purchase',
|
||||
|
||||
@@ -2,7 +2,12 @@ import type {
|
||||
DeferGoogleSubscriptionParams,
|
||||
DeferGoogleSubscriptionResponse,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatDeferGoogleSubscriptionTool: ToolConfig<
|
||||
@@ -37,38 +42,59 @@ export const revenuecatDeferGoogleSubscriptionTool: ToolConfig<
|
||||
},
|
||||
extendByDays: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of days to extend the subscription by (1-365)',
|
||||
description:
|
||||
'Number of days to extend the subscription by (1-365). Provide either extendByDays or expiryTimeMs.',
|
||||
},
|
||||
expiryTimeMs: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Absolute new expiry time in milliseconds since Unix epoch. Use instead of extendByDays to set an exact expiry.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/defer`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/subscriptions/${encodeURIComponent(params.productId.trim())}/defer`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
extend_by_days: params.extendByDays,
|
||||
}),
|
||||
body: (params) => {
|
||||
const hasExtend = params.extendByDays !== undefined
|
||||
const hasExpiry = params.expiryTimeMs !== undefined
|
||||
if (!hasExtend && !hasExpiry) {
|
||||
throw new Error('Provide either extendByDays or expiryTimeMs to defer a subscription')
|
||||
}
|
||||
if (hasExtend && hasExpiry) {
|
||||
throw new Error(
|
||||
'Provide only one of extendByDays or expiryTimeMs — they cannot be used together'
|
||||
)
|
||||
}
|
||||
const body: Record<string, unknown> = {}
|
||||
if (hasExpiry) body.expiry_time_ms = params.expiryTimeMs
|
||||
else if (hasExtend) {
|
||||
const days = params.extendByDays as number
|
||||
if (!Number.isFinite(days) || days < 1 || days > 365) {
|
||||
throw new Error('extendByDays must be an integer between 1 and 365')
|
||||
}
|
||||
body.extend_by_days = days
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
},
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { DeleteCustomerParams, DeleteCustomerResponse } from '@/tools/revenuecat/types'
|
||||
import { DELETE_OUTPUT_PROPERTIES } from '@/tools/revenuecat/types'
|
||||
import { DELETE_OUTPUT_PROPERTIES, throwIfRevenueCatError } from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatDeleteCustomerTool: ToolConfig<
|
||||
@@ -28,7 +28,7 @@ export const revenuecatDeleteCustomerTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -37,11 +37,24 @@ export const revenuecatDeleteCustomerTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
let body: Record<string, unknown> = {}
|
||||
try {
|
||||
body = await response.json()
|
||||
} catch {
|
||||
// Some delete responses have empty bodies — treat as success
|
||||
}
|
||||
return {
|
||||
success: response.ok,
|
||||
success: true,
|
||||
output: {
|
||||
deleted: response.ok,
|
||||
app_user_id: params?.appUserId ?? '',
|
||||
deleted:
|
||||
typeof body.deleted === 'boolean'
|
||||
? body.deleted
|
||||
: typeof body.was_deleted === 'boolean'
|
||||
? body.was_deleted
|
||||
: true,
|
||||
app_user_id:
|
||||
typeof body.app_user_id === 'string' ? body.app_user_id : (params?.appUserId ?? ''),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { CustomerResponse, GetCustomerParams } from '@/tools/revenuecat/types'
|
||||
import { METADATA_OUTPUT_PROPERTIES, SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
METADATA_OUTPUT_PROPERTIES,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatGetCustomerTool: ToolConfig<GetCustomerParams, CustomerResponse> = {
|
||||
@@ -25,7 +31,7 @@ export const revenuecatGetCustomerTool: ToolConfig<GetCustomerParams, CustomerRe
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -34,31 +40,49 @@ export const revenuecatGetCustomerTool: ToolConfig<GetCustomerParams, CustomerRe
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
const entitlements = subscriber.entitlements ?? {}
|
||||
const subscriptions = subscriber.subscriptions ?? {}
|
||||
const subscriberRaw = extractSubscriber(data)
|
||||
const subscriber = shapeSubscriber(subscriberRaw)
|
||||
const requestDate = (data?.value?.request_date ?? data?.request_date) as string | undefined
|
||||
const now = requestDate ? new Date(requestDate).getTime() : Date.now()
|
||||
|
||||
const activeEntitlements = Object.values(entitlements).filter(
|
||||
(e: unknown) => (e as Record<string, unknown>).is_active
|
||||
).length
|
||||
const activeSubscriptions = Object.keys(subscriptions).length
|
||||
const isActiveByDates = (
|
||||
expires: string | null | undefined,
|
||||
grace: string | null | undefined,
|
||||
refundedAt?: string | null | undefined
|
||||
) => {
|
||||
if (refundedAt) return false
|
||||
if (!expires) return true
|
||||
if (new Date(expires).getTime() > now) return true
|
||||
if (grace && new Date(grace).getTime() > now) return true
|
||||
return false
|
||||
}
|
||||
|
||||
const activeEntitlements = Object.values(subscriber.entitlements).filter((e) => {
|
||||
const ent = e as Record<string, unknown>
|
||||
return isActiveByDates(
|
||||
ent.expires_date as string | null | undefined,
|
||||
ent.grace_period_expires_date as string | null | undefined
|
||||
)
|
||||
}).length
|
||||
|
||||
const activeSubscriptions = Object.values(subscriber.subscriptions).filter((s) => {
|
||||
const sub = s as Record<string, unknown>
|
||||
return isActiveByDates(
|
||||
sub.expires_date as string | null | undefined,
|
||||
sub.grace_period_expires_date as string | null | undefined,
|
||||
sub.refunded_at as string | null | undefined
|
||||
)
|
||||
}).length
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
original_purchase_date: subscriber.original_purchase_date ?? null,
|
||||
management_url: subscriber.management_url ?? null,
|
||||
subscriptions: subscriptions,
|
||||
entitlements: entitlements,
|
||||
non_subscriptions: subscriber.non_subscriptions ?? {},
|
||||
},
|
||||
subscriber,
|
||||
metadata: {
|
||||
app_user_id: subscriber.original_app_user_id ?? '',
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
app_user_id: subscriber.original_app_user_id,
|
||||
first_seen: subscriber.first_seen,
|
||||
active_entitlements: activeEntitlements,
|
||||
active_subscriptions: activeSubscriptions,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { GrantEntitlementParams, GrantEntitlementResponse } from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatGrantEntitlementTool: ToolConfig<
|
||||
@@ -32,10 +37,17 @@ export const revenuecatGrantEntitlementTool: ToolConfig<
|
||||
},
|
||||
duration: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Duration of the entitlement (daily, three_day, weekly, monthly, two_month, three_month, six_month, yearly, lifetime)',
|
||||
'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',
|
||||
},
|
||||
endTimeMs: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Absolute end time in milliseconds since Unix epoch. Use instead of duration to grant the entitlement until a specific timestamp.',
|
||||
},
|
||||
startTimeMs: {
|
||||
type: 'number',
|
||||
@@ -48,32 +60,31 @@ export const revenuecatGrantEntitlementTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/entitlements/${encodeURIComponent(params.entitlementIdentifier)}/promotional`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/entitlements/${encodeURIComponent(params.entitlementIdentifier.trim())}/promotional`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = { duration: params.duration }
|
||||
if (!params.duration && params.endTimeMs === undefined) {
|
||||
throw new Error('Provide either duration or endTimeMs to grant a promotional entitlement')
|
||||
}
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.endTimeMs !== undefined) body.end_time_ms = params.endTimeMs
|
||||
else if (params.duration) body.duration = params.duration
|
||||
if (params.startTimeMs !== undefined) body.start_time_ms = params.startTimeMs
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
},
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ListOfferingsParams, ListOfferingsResponse } from '@/tools/revenue
|
||||
import {
|
||||
OFFERING_OUTPUT_PROPERTIES,
|
||||
OFFERINGS_METADATA_OUTPUT_PROPERTIES,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -34,7 +35,7 @@ export const revenuecatListOfferingsTool: ToolConfig<ListOfferingsParams, ListOf
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/offerings`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/offerings`,
|
||||
method: 'GET',
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {
|
||||
@@ -49,6 +50,7 @@ export const revenuecatListOfferingsTool: ToolConfig<ListOfferingsParams, ListOf
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const offerings = data.offerings ?? []
|
||||
const currentOfferingId = data.current_offering_id ?? null
|
||||
|
||||
@@ -2,7 +2,12 @@ import type {
|
||||
RefundGoogleSubscriptionParams,
|
||||
RefundGoogleSubscriptionResponse,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatRefundGoogleSubscriptionTool: ToolConfig<
|
||||
@@ -11,7 +16,8 @@ export const revenuecatRefundGoogleSubscriptionTool: ToolConfig<
|
||||
> = {
|
||||
id: 'revenuecat_refund_google_subscription',
|
||||
name: 'RevenueCat Refund Google Subscription',
|
||||
description: 'Refund and optionally revoke a Google Play subscription (Google Play only)',
|
||||
description:
|
||||
'Refund a specific store transaction by its store transaction identifier and revoke access (subscription or non-subscription, last 365 days)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
@@ -27,17 +33,18 @@ export const revenuecatRefundGoogleSubscriptionTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The app user ID of the subscriber',
|
||||
},
|
||||
productId: {
|
||||
storeTransactionId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The Google Play product identifier of the subscription to refund',
|
||||
description:
|
||||
'The store transaction identifier of the purchase to refund (e.g., GPA.3309-9122-6177-45730 for Google Play)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/refund`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/transactions/${encodeURIComponent(params.storeTransactionId.trim())}/refund`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -46,18 +53,12 @@ export const revenuecatRefundGoogleSubscriptionTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
},
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { RevokeEntitlementParams, RevokeEntitlementResponse } from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatRevokeEntitlementTool: ToolConfig<
|
||||
@@ -34,7 +39,7 @@ export const revenuecatRevokeEntitlementTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/entitlements/${encodeURIComponent(params.entitlementIdentifier)}/revoke_promotionals`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/entitlements/${encodeURIComponent(params.entitlementIdentifier.trim())}/revoke_promotionals`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -43,18 +48,12 @@ export const revenuecatRevokeEntitlementTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
},
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,12 @@ import type {
|
||||
RevokeGoogleSubscriptionParams,
|
||||
RevokeGoogleSubscriptionResponse,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatRevokeGoogleSubscriptionTool: ToolConfig<
|
||||
@@ -38,7 +43,7 @@ export const revenuecatRevokeGoogleSubscriptionTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/revoke`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/subscriptions/${encodeURIComponent(params.productId.trim())}/revoke`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -47,18 +52,12 @@ export const revenuecatRevokeGoogleSubscriptionTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json()
|
||||
const subscriber = data.subscriber ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: subscriber.first_seen ?? '',
|
||||
original_app_user_id: subscriber.original_app_user_id ?? '',
|
||||
subscriptions: subscriber.subscriptions ?? {},
|
||||
entitlements: subscriber.entitlements ?? {},
|
||||
},
|
||||
subscriber: shapeSubscriber(extractSubscriber(data)),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -76,28 +76,9 @@ export const SUBSCRIPTION_OUTPUT_PROPERTIES = {
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const ENTITLEMENT_OUTPUT_PROPERTIES = {
|
||||
grant_date: { type: 'string', description: 'ISO 8601 grant date', optional: true },
|
||||
expires_date: { type: 'string', description: 'ISO 8601 expiration date', optional: true },
|
||||
product_identifier: { type: 'string', description: 'Product identifier', optional: true },
|
||||
is_active: { type: 'boolean', description: 'Whether the entitlement is active' },
|
||||
will_renew: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the entitlement will renew',
|
||||
optional: true,
|
||||
},
|
||||
period_type: {
|
||||
expires_date: {
|
||||
type: 'string',
|
||||
description: 'Period type (normal, trial, intro, promotional)',
|
||||
optional: true,
|
||||
},
|
||||
purchase_date: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date of the latest purchase or renewal',
|
||||
optional: true,
|
||||
},
|
||||
store: {
|
||||
type: 'string',
|
||||
description: 'Store the entitlement was granted from',
|
||||
description: 'ISO 8601 expiration date (null for non-expiring entitlements)',
|
||||
optional: true,
|
||||
},
|
||||
grace_period_expires_date: {
|
||||
@@ -105,14 +86,30 @@ export const ENTITLEMENT_OUTPUT_PROPERTIES = {
|
||||
description: 'ISO 8601 grace period expiration date',
|
||||
optional: true,
|
||||
},
|
||||
product_identifier: { type: 'string', description: 'Product identifier', optional: true },
|
||||
purchase_date: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date of the latest purchase or renewal',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SUBSCRIBER_OUTPUT_PROPERTIES = {
|
||||
first_seen: { type: 'string', description: 'ISO 8601 date when subscriber was first seen' },
|
||||
last_seen: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when subscriber was last seen',
|
||||
optional: true,
|
||||
},
|
||||
original_app_user_id: { type: 'string', description: 'Original app user ID' },
|
||||
original_application_version: {
|
||||
type: 'string',
|
||||
description: 'iOS only. First App Store version of your app the customer installed',
|
||||
optional: true,
|
||||
},
|
||||
original_purchase_date: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date of original purchase',
|
||||
description: 'iOS only. Date the app was first purchased/downloaded',
|
||||
optional: true,
|
||||
},
|
||||
management_url: {
|
||||
@@ -135,6 +132,17 @@ export const SUBSCRIBER_OUTPUT_PROPERTIES = {
|
||||
description: 'Map of non-subscription product identifiers to arrays of purchase objects',
|
||||
optional: true,
|
||||
},
|
||||
other_purchases: {
|
||||
type: 'object',
|
||||
description: 'Other purchases attached to the subscriber',
|
||||
optional: true,
|
||||
},
|
||||
subscriber_attributes: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Custom attributes set on the subscriber. Only returned when using a secret API key',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SUBSCRIBER_OUTPUT: OutputProperty = {
|
||||
@@ -186,6 +194,51 @@ export const OFFERINGS_METADATA_OUTPUT_PROPERTIES = {
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
/**
|
||||
* Several RevenueCat v1 endpoints (post receipts, update attributes, revoke promotionals,
|
||||
* defer/refund/revoke Google subscriptions) wrap responses in `{ value: { request_date, subscriber } }`.
|
||||
* GET customer info returns the same payload unwrapped. This helper handles both shapes.
|
||||
*/
|
||||
export function extractSubscriber(data: unknown): Record<string, unknown> {
|
||||
if (!data || typeof data !== 'object') return {}
|
||||
const root = data as Record<string, unknown>
|
||||
const wrapped = root.value as Record<string, unknown> | undefined
|
||||
const subscriber = (wrapped?.subscriber ?? root.subscriber) as Record<string, unknown> | undefined
|
||||
return subscriber ?? {}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /v1/receipts may return a top-level `customer` object alongside `subscriber`.
|
||||
* Returns null when not present (e.g., wrapped envelope responses).
|
||||
*/
|
||||
export function extractCustomer(data: unknown): Record<string, unknown> | null {
|
||||
if (!data || typeof data !== 'object') return null
|
||||
const customer = (data as Record<string, unknown>).customer
|
||||
return customer && typeof customer === 'object' ? (customer as Record<string, unknown>) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a RevenueCat REST API error response into a meaningful Error.
|
||||
* RevenueCat returns `{ code, message }` on 4xx/5xx.
|
||||
*/
|
||||
export async function throwIfRevenueCatError(response: Response): Promise<void> {
|
||||
if (response.ok) return
|
||||
let message = `RevenueCat API error (${response.status})`
|
||||
try {
|
||||
const body = await response.clone().json()
|
||||
if (body && typeof body === 'object') {
|
||||
const m = (body as Record<string, unknown>).message
|
||||
const c = (body as Record<string, unknown>).code
|
||||
if (typeof m === 'string' && m.length > 0) {
|
||||
message = c ? `${m} (code ${c})` : m
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Body not JSON — fall back to status-only message
|
||||
}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base params interface for RevenueCat API calls
|
||||
*/
|
||||
@@ -204,7 +257,8 @@ export interface DeleteCustomerParams extends RevenueCatBaseParams {
|
||||
export interface GrantEntitlementParams extends RevenueCatBaseParams {
|
||||
appUserId: string
|
||||
entitlementIdentifier: string
|
||||
duration: string
|
||||
duration?: string
|
||||
endTimeMs?: number
|
||||
startTimeMs?: number
|
||||
}
|
||||
|
||||
@@ -221,11 +275,16 @@ export interface ListOfferingsParams extends RevenueCatBaseParams {
|
||||
export interface CreatePurchaseParams extends RevenueCatBaseParams {
|
||||
appUserId: string
|
||||
fetchToken: string
|
||||
productId: string
|
||||
productId?: string
|
||||
price?: number
|
||||
currency?: string
|
||||
isRestore?: boolean
|
||||
platform?: string
|
||||
presentedOfferingIdentifier?: string
|
||||
paymentMode?: string
|
||||
introductoryPrice?: number
|
||||
attributes?: string
|
||||
updatedAtMs?: number
|
||||
platform: string
|
||||
}
|
||||
|
||||
export interface UpdateSubscriberAttributesParams extends RevenueCatBaseParams {
|
||||
@@ -236,12 +295,13 @@ export interface UpdateSubscriberAttributesParams extends RevenueCatBaseParams {
|
||||
export interface DeferGoogleSubscriptionParams extends RevenueCatBaseParams {
|
||||
appUserId: string
|
||||
productId: string
|
||||
extendByDays: number
|
||||
extendByDays?: number
|
||||
expiryTimeMs?: number
|
||||
}
|
||||
|
||||
export interface RefundGoogleSubscriptionParams extends RevenueCatBaseParams {
|
||||
appUserId: string
|
||||
productId: string
|
||||
storeTransactionId: string
|
||||
}
|
||||
|
||||
export interface RevokeGoogleSubscriptionParams extends RevenueCatBaseParams {
|
||||
@@ -249,17 +309,39 @@ export interface RevokeGoogleSubscriptionParams extends RevenueCatBaseParams {
|
||||
productId: string
|
||||
}
|
||||
|
||||
export interface RevenueCatSubscriber {
|
||||
first_seen: string
|
||||
last_seen: string | null
|
||||
original_app_user_id: string
|
||||
original_application_version: string | null
|
||||
original_purchase_date: string | null
|
||||
management_url: string | null
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
non_subscriptions: Record<string, unknown>
|
||||
other_purchases: Record<string, unknown>
|
||||
subscriber_attributes: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export function shapeSubscriber(raw: Record<string, unknown>): RevenueCatSubscriber {
|
||||
return {
|
||||
first_seen: (raw.first_seen as string) ?? '',
|
||||
last_seen: (raw.last_seen as string | null) ?? null,
|
||||
original_app_user_id: (raw.original_app_user_id as string) ?? '',
|
||||
original_application_version: (raw.original_application_version as string | null) ?? null,
|
||||
original_purchase_date: (raw.original_purchase_date as string | null) ?? null,
|
||||
management_url: (raw.management_url as string | null) ?? null,
|
||||
subscriptions: (raw.subscriptions as Record<string, unknown>) ?? {},
|
||||
entitlements: (raw.entitlements as Record<string, unknown>) ?? {},
|
||||
non_subscriptions: (raw.non_subscriptions as Record<string, unknown>) ?? {},
|
||||
other_purchases: (raw.other_purchases as Record<string, unknown>) ?? {},
|
||||
subscriber_attributes: (raw.subscriber_attributes as Record<string, unknown> | null) ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export interface CustomerResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
original_purchase_date: string | null
|
||||
management_url: string | null
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
non_subscriptions: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
metadata: {
|
||||
app_user_id: string
|
||||
first_seen: string
|
||||
@@ -278,23 +360,13 @@ export interface DeleteCustomerResponse extends ToolResponse {
|
||||
|
||||
export interface GrantEntitlementResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
export interface RevokeEntitlementResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,13 +390,8 @@ export interface ListOfferingsResponse extends ToolResponse {
|
||||
|
||||
export interface CreatePurchaseResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
non_subscriptions: Record<string, unknown>
|
||||
}
|
||||
customer: Record<string, unknown> | null
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,39 +399,25 @@ export interface UpdateSubscriberAttributesResponse extends ToolResponse {
|
||||
output: {
|
||||
updated: boolean
|
||||
app_user_id: string
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeferGoogleSubscriptionResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
export interface RefundGoogleSubscriptionResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
export interface RevokeGoogleSubscriptionResponse extends ToolResponse {
|
||||
output: {
|
||||
subscriber: {
|
||||
first_seen: string
|
||||
original_app_user_id: string
|
||||
subscriptions: Record<string, unknown>
|
||||
entitlements: Record<string, unknown>
|
||||
}
|
||||
subscriber: RevenueCatSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@ import type {
|
||||
UpdateSubscriberAttributesParams,
|
||||
UpdateSubscriberAttributesResponse,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import {
|
||||
extractSubscriber,
|
||||
SUBSCRIBER_OUTPUT,
|
||||
shapeSubscriber,
|
||||
throwIfRevenueCatError,
|
||||
} from '@/tools/revenuecat/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const revenuecatUpdateSubscriberAttributesTool: ToolConfig<
|
||||
@@ -32,13 +38,13 @@ export const revenuecatUpdateSubscriberAttributesTool: ToolConfig<
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'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"}}',
|
||||
'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}}',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/attributes`,
|
||||
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId.trim())}/attributes`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
@@ -52,11 +58,15 @@ export const revenuecatUpdateSubscriberAttributesTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
await throwIfRevenueCatError(response)
|
||||
const data = await response.json().catch(() => ({}))
|
||||
const subscriber = shapeSubscriber(extractSubscriber(data))
|
||||
return {
|
||||
success: response.ok,
|
||||
success: true,
|
||||
output: {
|
||||
updated: response.ok,
|
||||
app_user_id: params?.appUserId ?? '',
|
||||
updated: true,
|
||||
app_user_id: subscriber.original_app_user_id || (params?.appUserId ?? ''),
|
||||
subscriber,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -70,5 +80,9 @@ export const revenuecatUpdateSubscriberAttributesTool: ToolConfig<
|
||||
type: 'string',
|
||||
description: 'The app user ID of the updated subscriber',
|
||||
},
|
||||
subscriber: {
|
||||
...SUBSCRIBER_OUTPUT,
|
||||
description: 'The updated subscriber object after applying the attribute changes',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user