@shware/purchase 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +341 -0
- package/dist/app-store/config.d.cts +2 -2
- package/dist/app-store/config.d.cts.map +1 -1
- package/dist/app-store/config.d.mts +2 -2
- package/dist/app-store/config.d.mts.map +1 -1
- package/dist/app-store/handler.d.cts +3 -3
- package/dist/app-store/handler.d.cts.map +1 -1
- package/dist/app-store/handler.d.mts +3 -3
- package/dist/app-store/handler.d.mts.map +1 -1
- package/dist/app-store/mapper.d.cts +2 -3
- package/dist/app-store/mapper.d.cts.map +1 -1
- package/dist/app-store/mapper.d.mts +2 -3
- package/dist/app-store/mapper.d.mts.map +1 -1
- package/dist/google-play/config.d.cts +2 -2
- package/dist/google-play/config.d.cts.map +1 -1
- package/dist/google-play/config.d.mts +2 -2
- package/dist/google-play/config.d.mts.map +1 -1
- package/dist/google-play/mapper.d.cts +4 -5
- package/dist/google-play/mapper.d.cts.map +1 -1
- package/dist/google-play/mapper.d.mts +4 -5
- package/dist/google-play/mapper.d.mts.map +1 -1
- package/dist/google-play/real-time-developer-notification.d.cts +16 -17
- package/dist/google-play/real-time-developer-notification.d.cts.map +1 -1
- package/dist/google-play/real-time-developer-notification.d.mts +16 -17
- package/dist/google-play/real-time-developer-notification.d.mts.map +1 -1
- package/dist/stripe/config.d.cts +4 -4
- package/dist/stripe/config.d.cts.map +1 -1
- package/dist/stripe/config.d.mts +4 -4
- package/dist/stripe/config.d.mts.map +1 -1
- package/dist/stripe/handler.d.cts +2 -2
- package/dist/stripe/handler.d.cts.map +1 -1
- package/dist/stripe/handler.d.mts +2 -2
- package/dist/stripe/handler.d.mts.map +1 -1
- package/dist/stripe/mapper.d.cts +22 -23
- package/dist/stripe/mapper.d.cts.map +1 -1
- package/dist/stripe/mapper.d.mts +22 -23
- package/dist/stripe/mapper.d.mts.map +1 -1
- package/dist/stripe/metadata.d.cts +1 -2
- package/dist/stripe/metadata.d.cts.map +1 -1
- package/dist/stripe/metadata.d.mts +1 -2
- package/dist/stripe/metadata.d.mts.map +1 -1
- package/dist/stripe/pending-checkout.d.cts +3 -4
- package/dist/stripe/pending-checkout.d.cts.map +1 -1
- package/dist/stripe/pending-checkout.d.mts +3 -4
- package/dist/stripe/pending-checkout.d.mts.map +1 -1
- package/dist/stripe/schema.d.cts +5 -5
- package/dist/stripe/schema.d.cts.map +1 -1
- package/dist/stripe/schema.d.mts +5 -5
- package/dist/stripe/schema.d.mts.map +1 -1
- package/dist/subscription/index.d.cts +12 -13
- package/dist/subscription/index.d.cts.map +1 -1
- package/dist/subscription/index.d.mts +12 -13
- package/dist/subscription/index.d.mts.map +1 -1
- package/dist/types.d.cts +2 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +2 -3
- package/dist/types.d.mts.map +1 -1
- package/package.json +9 -7
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# @shware/purchase
|
|
2
|
+
|
|
3
|
+
Shared building blocks for selling subscriptions and credits on Stripe, the App Store and Google
|
|
4
|
+
Play. The root export holds a platform-neutral subscription model (statuses, cancellation details)
|
|
5
|
+
and the three product catalogs (`StripeConfig`, `AppStoreConfig`, `GooglePlayConfig`) that map
|
|
6
|
+
store product ids to your plans, billing periods and credit grants. The platform subpaths add
|
|
7
|
+
typed event dispatchers and mappers from each store's payloads onto that model. Verifying webhook
|
|
8
|
+
signatures and calling store APIs stays in your app, with the official SDKs.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @shware/purchase zod
|
|
14
|
+
# optional peers, only for the subpaths you use
|
|
15
|
+
pnpm add stripe # @shware/purchase/stripe (^22.6.2 || ^23.0.0)
|
|
16
|
+
pnpm add @apple/app-store-server-library # @shware/purchase/app-store (^3.1.0)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Entry points
|
|
20
|
+
|
|
21
|
+
| Import path | Contents |
|
|
22
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| `@shware/purchase` | Subscription model (`PLATFORMS`, `SUBSCRIPTION_STATUSES`, `AVAILABLE_STATUSES`, cancellation types) and the three configs |
|
|
24
|
+
| `@shware/purchase/stripe` | `StripeEventHandler`, Stripe mappers, checkout schemas, `METADATA_KEYS`, analytics helpers, browser pending-checkout helpers |
|
|
25
|
+
| `@shware/purchase/app-store` | `AppStoreNotificationHandler` for App Store Server Notifications V2, `mapCancellationDetails` |
|
|
26
|
+
| `@shware/purchase/google-play` | Real-time developer notification (RTDN) types and enums, subscription state and cancellation mappers |
|
|
27
|
+
|
|
28
|
+
## Subscription model
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import {
|
|
32
|
+
AVAILABLE_STATUSES,
|
|
33
|
+
PLATFORMS,
|
|
34
|
+
SUBSCRIPTION_STATUSES,
|
|
35
|
+
type CancellationDetails,
|
|
36
|
+
type Platform,
|
|
37
|
+
type SubscriptionStatus,
|
|
38
|
+
} from '@shware/purchase';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `PLATFORMS`: `'APPLE' | 'GOOGLE' | 'STRIPE'`.
|
|
42
|
+
- `SUBSCRIPTION_STATUSES`: the union of every store's states (`TRIALING`, `ACTIVE`,
|
|
43
|
+
`IN_GRACE_PERIOD`, `PAST_DUE`, `ON_HOLD`, `PAUSED`, `CANCELED`, `EXPIRED`, ...). Each platform
|
|
44
|
+
mapper returns one of these.
|
|
45
|
+
- `AVAILABLE_STATUSES`: `ACTIVE`, `TRIALING`, `IN_GRACE_PERIOD`, the statuses that grant access.
|
|
46
|
+
- `CancellationDetails`: why a subscription stopped renewing, one record for all platforms:
|
|
47
|
+
`initiator` (`CANCELLATION_INITIATORS`: `user`, `system`, `developer`), the store's own `reason`,
|
|
48
|
+
`feedback` (`CANCELLATION_FEEDBACKS`), `comment` and `resubscribeIntent` (`RESUBSCRIBE_INTENTS`).
|
|
49
|
+
|
|
50
|
+
The constants are `as const` arrays, so they can back database enums directly:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { pgEnum } from 'drizzle-orm/pg-core';
|
|
54
|
+
import { PLATFORMS, SUBSCRIPTION_STATUSES } from '@shware/purchase';
|
|
55
|
+
|
|
56
|
+
export const platform = pgEnum('platform', PLATFORMS);
|
|
57
|
+
export const subscriptionStatus = pgEnum('subscription_status', SUBSCRIPTION_STATUSES);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const entitled = (AVAILABLE_STATUSES as readonly SubscriptionStatus[]).includes(sub.status);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Product catalogs
|
|
65
|
+
|
|
66
|
+
Each config is built with a fluent, type-checked chain. Type parameters are the product id
|
|
67
|
+
namespace, your plan type and your billing period type; product ids must start with the namespace,
|
|
68
|
+
and duplicate products or plan/period pairs fail at compile time and at runtime. Credit metadata
|
|
69
|
+
is `{ credits: number; expiresIn: string }` where `expiresIn` is an
|
|
70
|
+
[`ms`](https://github.com/vercel/ms) duration.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { AppStoreConfig, GooglePlayConfig, StripeConfig } from '@shware/purchase';
|
|
74
|
+
|
|
75
|
+
type Plan = 'STARTER' | 'PRO';
|
|
76
|
+
type BillingPeriod = 'MONTHLY' | 'YEARLY';
|
|
77
|
+
|
|
78
|
+
export const stripe = StripeConfig.create<'com.example.stripe', Plan, BillingPeriod>({
|
|
79
|
+
mode: env.STRIPE_SECRET_KEY.startsWith('sk_test_') ? 'test' : 'live',
|
|
80
|
+
allowPromotionCodes: false,
|
|
81
|
+
cancellationCouponId: 'retention_30pct_3mo',
|
|
82
|
+
returnUrl: 'https://example.com',
|
|
83
|
+
cancelUrl: 'https://example.com',
|
|
84
|
+
successUrl: 'https://example.com/payments/success?session_id={CHECKOUT_SESSION_ID}',
|
|
85
|
+
})
|
|
86
|
+
// one-time product: no plan or period
|
|
87
|
+
.product('com.example.stripe.credit.starter')
|
|
88
|
+
.price('price_live_credits', { credits: 10, expiresIn: '1 year' })
|
|
89
|
+
.default('price_live_credits', { test: 'price_test_credits' })
|
|
90
|
+
// subscription: plan + billing period
|
|
91
|
+
.product('com.example.stripe.sub.monthly.starter', 'STARTER', 'MONTHLY')
|
|
92
|
+
.price('price_live_starter_v2', { credits: 100, expiresIn: '30 days' })
|
|
93
|
+
.price('price_live_starter_v1', { credits: 100, expiresIn: '30 days' }) // grandfathered
|
|
94
|
+
.default('price_live_starter_v2', { test: 'price_test_starter' });
|
|
95
|
+
|
|
96
|
+
export const appStore = AppStoreConfig.create<'com.example.appstore', Plan, BillingPeriod>({
|
|
97
|
+
appId: '1234567890',
|
|
98
|
+
bundleId: 'com.example.app',
|
|
99
|
+
})
|
|
100
|
+
.consumable('com.example.appstore.credit.starter', { credits: 10, expiresIn: '365d' })
|
|
101
|
+
.subscription('STARTER', 'MONTHLY')
|
|
102
|
+
.product('com.example.appstore.sub.monthly.starter.v1', { credits: 100, expiresIn: '30d' })
|
|
103
|
+
.default('com.example.appstore.sub.monthly.starter.v1');
|
|
104
|
+
|
|
105
|
+
export const googlePlay = GooglePlayConfig.create<'com.example.googleplay', Plan, BillingPeriod>({
|
|
106
|
+
package: 'com.example.app',
|
|
107
|
+
audience: 'api.example.com', // expected audience of the RTDN push OIDC token
|
|
108
|
+
})
|
|
109
|
+
.subscription('com.example.googleplay.sub.starter')
|
|
110
|
+
.basePlan('monthly-v1', 'STARTER', 'MONTHLY', { credits: 100, expiresIn: '30d' })
|
|
111
|
+
.basePlan('yearly-v1', 'STARTER', 'YEARLY', { credits: 1200, expiresIn: '365d' })
|
|
112
|
+
.default(['monthly-v1', 'yearly-v1'])
|
|
113
|
+
.onetime('com.example.googleplay.credit.starter', { credits: 10, expiresIn: '365d' });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Lookups:
|
|
117
|
+
|
|
118
|
+
| Config | Methods |
|
|
119
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
|
+
| `StripeConfig` | `productIds`, `getPriceId(productId)`, `getPlan`, `getBillingPeriod`, `getMode`, `getCreditAmount(productId, priceId?)`, `getCreditExpiresAt(productId, priceId?)` |
|
|
121
|
+
| `AppStoreConfig` | `productIds`, `getDefaultProductId(plan, billingPeriod)`, `getPlan`, `getBillingPeriod`, `getMode`, `getCreditAmount`, `getCreditExpiresAt` |
|
|
122
|
+
| `GooglePlayConfig` | `productIds`, `getDefaultPlanIds(productId)`, `getPlan(productId, planId)`, `getBillingPeriod(productId, planId)`, `getMode`, `getCreditAmount(productId, planId?)`, `getCreditExpiresAt(productId, planId?)` |
|
|
123
|
+
|
|
124
|
+
In Stripe test mode the `test` twin of the default price is what `getPriceId` returns, and it
|
|
125
|
+
resolves to the default price's credits. Non-default prices, products and base plans stay declared
|
|
126
|
+
so existing subscribers keep resolving.
|
|
127
|
+
|
|
128
|
+
## Stripe
|
|
129
|
+
|
|
130
|
+
`@shware/purchase/stripe` (requires `stripe`).
|
|
131
|
+
|
|
132
|
+
### Webhooks
|
|
133
|
+
|
|
134
|
+
Verify the signature with the Stripe SDK, then dispatch with `StripeEventHandler`. `on` is typed
|
|
135
|
+
per event, so each handler receives the right `event.data.object`; `process` throws for an event
|
|
136
|
+
type with no handler.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import Stripe from 'stripe';
|
|
140
|
+
import {
|
|
141
|
+
METADATA_KEYS,
|
|
142
|
+
StripeEventHandler,
|
|
143
|
+
mapCancellationDetails,
|
|
144
|
+
mapSubscriptionStatus,
|
|
145
|
+
mapTime,
|
|
146
|
+
} from '@shware/purchase/stripe';
|
|
147
|
+
|
|
148
|
+
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
|
|
149
|
+
|
|
150
|
+
export async function processStripeEvent(signature: string, body: string) {
|
|
151
|
+
const event = await stripe.webhooks.constructEventAsync(
|
|
152
|
+
body,
|
|
153
|
+
signature,
|
|
154
|
+
env.STRIPE_WEBHOOK_SECRET
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
await StripeEventHandler.create()
|
|
158
|
+
.on('checkout.session.completed', handleCheckoutSessionCompleted)
|
|
159
|
+
.on('invoice.payment_succeeded', handleInvoicePaymentSucceeded)
|
|
160
|
+
.on('customer.subscription.created', handleSubscriptionEvent)
|
|
161
|
+
.on('customer.subscription.updated', handleSubscriptionEvent)
|
|
162
|
+
.on('customer.subscription.deleted', handleSubscriptionEvent)
|
|
163
|
+
.process(event);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function handleSubscriptionEvent(sub: Stripe.Subscription) {
|
|
167
|
+
const userId = sub.metadata[METADATA_KEYS.USER_ID];
|
|
168
|
+
const productId = sub.metadata[METADATA_KEYS.PRODUCT_ID];
|
|
169
|
+
await saveSubscription({
|
|
170
|
+
userId,
|
|
171
|
+
platform: 'STRIPE',
|
|
172
|
+
plan: config.stripe.getPlan(productId),
|
|
173
|
+
billingPeriod: config.stripe.getBillingPeriod(productId),
|
|
174
|
+
status: mapSubscriptionStatus(sub.status),
|
|
175
|
+
startedAt: mapTime(sub.start_date),
|
|
176
|
+
cancellationDetails: mapCancellationDetails(sub),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Checkout
|
|
182
|
+
|
|
183
|
+
`createCheckoutSessionSchema(productIds)` is a `zod/mini` schema for the create-checkout request
|
|
184
|
+
(`productId`, `quantity` default 1, optional `cancelUrl` / `successUrl`). `METADATA_KEYS` names
|
|
185
|
+
the metadata fields (`user_id`, `product_id`, `referral_code`, ...) the webhook reads back.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { zValidator } from '@hono/zod-validator';
|
|
189
|
+
import { METADATA_KEYS, createCheckoutSessionSchema } from '@shware/purchase/stripe';
|
|
190
|
+
|
|
191
|
+
app.post(
|
|
192
|
+
'/v1/checkout/sessions',
|
|
193
|
+
zValidator('json', createCheckoutSessionSchema(config.stripe.productIds)),
|
|
194
|
+
async (c) => {
|
|
195
|
+
const { productId, quantity, cancelUrl, successUrl } = c.req.valid('json');
|
|
196
|
+
const userId = await getUserId(c);
|
|
197
|
+
const session = await stripe.checkout.sessions.create({
|
|
198
|
+
mode: config.stripe.getMode(productId),
|
|
199
|
+
line_items: [{ price: config.stripe.getPriceId(productId), quantity }],
|
|
200
|
+
metadata: { [METADATA_KEYS.USER_ID]: userId, [METADATA_KEYS.PRODUCT_ID]: productId },
|
|
201
|
+
allow_promotion_codes: config.stripe.allowPromotionCodes,
|
|
202
|
+
success_url: successUrl ?? config.stripe.successUrl,
|
|
203
|
+
cancel_url: cancelUrl ?? config.stripe.cancelUrl,
|
|
204
|
+
});
|
|
205
|
+
return c.json({ id: session.id, url: session.url });
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`cancellationDetailsSchema` validates what your own cancel dialog sends (`comment`, `feedback`,
|
|
211
|
+
`resubscribeIntent`).
|
|
212
|
+
|
|
213
|
+
### Mappers
|
|
214
|
+
|
|
215
|
+
Plain-JSON views of Stripe objects for API responses: `mapCheckoutSession`, `mapLineItem`,
|
|
216
|
+
`mapPrice`, `mapInvoice`, `mapPaymentIntent`, `mapCharge`, plus `mapSubscriptionStatus`,
|
|
217
|
+
`mapCancellationDetails` and `mapTime` (Unix seconds to ISO string, `null` passes through).
|
|
218
|
+
|
|
219
|
+
### Browser helpers
|
|
220
|
+
|
|
221
|
+
These run in the browser and have no runtime dependency on `stripe`:
|
|
222
|
+
|
|
223
|
+
- `price(value, currency)` / `minorUnits(currency)`: convert minor units to a display amount,
|
|
224
|
+
aware of zero-decimal currencies.
|
|
225
|
+
- `getBeginCheckoutProperties(productPrice)` and `getPurchaseProperties(checkoutSession)`:
|
|
226
|
+
GA4-style `begin_checkout` / `purchase` event properties.
|
|
227
|
+
- `addPendingCheckout(sessionId)`, `getPendingCheckouts()`, `removePendingCheckout(sessionId)`:
|
|
228
|
+
a `localStorage` list (one-hour TTL) of checkouts to re-confirm if the user closed the tab before
|
|
229
|
+
returning to the success page.
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import {
|
|
233
|
+
addPendingCheckout,
|
|
234
|
+
getPendingCheckouts,
|
|
235
|
+
removePendingCheckout,
|
|
236
|
+
} from '@shware/purchase/stripe';
|
|
237
|
+
|
|
238
|
+
// before redirecting to Stripe
|
|
239
|
+
addPendingCheckout(session.id);
|
|
240
|
+
|
|
241
|
+
// on app start
|
|
242
|
+
for (const sessionId of getPendingCheckouts()) {
|
|
243
|
+
await confirmCheckoutSession(sessionId);
|
|
244
|
+
removePendingCheckout(sessionId);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## App Store
|
|
249
|
+
|
|
250
|
+
`@shware/purchase/app-store` (requires `@apple/app-store-server-library`).
|
|
251
|
+
|
|
252
|
+
Decode and verify the notification with Apple's `SignedDataVerifier`, then dispatch the decoded
|
|
253
|
+
event by `notificationType` with `AppStoreNotificationHandler`. Branch on `subtype` inside each
|
|
254
|
+
handler.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { NotificationTypeV2, type SignedDataVerifier } from '@apple/app-store-server-library';
|
|
258
|
+
import {
|
|
259
|
+
type AppStoreNotificationEvent,
|
|
260
|
+
AppStoreNotificationHandler,
|
|
261
|
+
mapCancellationDetails,
|
|
262
|
+
} from '@shware/purchase/app-store';
|
|
263
|
+
|
|
264
|
+
export async function processAppStoreNotification(verifier: SignedDataVerifier, body: string) {
|
|
265
|
+
const payload = await verifier.verifyAndDecodeNotification(body);
|
|
266
|
+
const { signedTransactionInfo, signedRenewalInfo } = payload.data ?? {};
|
|
267
|
+
const transaction = signedTransactionInfo
|
|
268
|
+
? await verifier.verifyAndDecodeTransaction(signedTransactionInfo)
|
|
269
|
+
: null;
|
|
270
|
+
const renewalInfo = signedRenewalInfo
|
|
271
|
+
? await verifier.verifyAndDecodeRenewalInfo(signedRenewalInfo)
|
|
272
|
+
: null;
|
|
273
|
+
|
|
274
|
+
await AppStoreNotificationHandler.create()
|
|
275
|
+
.on(NotificationTypeV2.SUBSCRIBED, handleSubscribed)
|
|
276
|
+
.on(NotificationTypeV2.DID_RENEW, handleRenewed)
|
|
277
|
+
.on(NotificationTypeV2.DID_CHANGE_RENEWAL_STATUS, handleRenewalStatusChanged)
|
|
278
|
+
.on(NotificationTypeV2.EXPIRED, handleExpired)
|
|
279
|
+
.on(NotificationTypeV2.REFUND, handleRefunded)
|
|
280
|
+
.process({ payload, transaction, renewalInfo });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function handleRenewalStatusChanged({ payload, renewalInfo }: AppStoreNotificationEvent) {
|
|
284
|
+
const cancellationDetails = mapCancellationDetails({
|
|
285
|
+
notificationType: payload.notificationType!,
|
|
286
|
+
subtype: payload.subtype,
|
|
287
|
+
expirationIntent: renewalInfo?.expirationIntent,
|
|
288
|
+
});
|
|
289
|
+
// null when auto-renew was turned back on
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Map `transaction.productId` to your plan with `config.appStore.getPlan(productId)` and
|
|
294
|
+
`getBillingPeriod(productId)`, and grant credits with `getCreditAmount` / `getCreditExpiresAt`.
|
|
295
|
+
|
|
296
|
+
## Google Play
|
|
297
|
+
|
|
298
|
+
`@shware/purchase/google-play` (no peer dependency).
|
|
299
|
+
|
|
300
|
+
It provides the RTDN payload types (`PubsubMessage`, `DeveloperNotification`,
|
|
301
|
+
`SubscriptionNotification`, `OneTimeProductNotification`, `VoidedPurchaseNotification`,
|
|
302
|
+
`TestNotification`), the numeric enums (`SubscriptionNotificationType`,
|
|
303
|
+
`OneTimeProductNotificationType`, `VoidedPurchaseProductType`, `VoidedPurchaseRefundType`), and
|
|
304
|
+
mappers for the `purchases.subscriptionsv2` resource. Verify the Pub/Sub push token (its audience
|
|
305
|
+
is `config.googlePlay.audience`) and fetch the purchase with the Android Publisher API yourself.
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
import {
|
|
309
|
+
type DeveloperNotification,
|
|
310
|
+
type GooglePlaySubscriptionState,
|
|
311
|
+
type PubsubMessage,
|
|
312
|
+
mapCanceledAt,
|
|
313
|
+
mapCancellationDetails,
|
|
314
|
+
mapSubscriptionStatus,
|
|
315
|
+
} from '@shware/purchase/google-play';
|
|
316
|
+
|
|
317
|
+
export async function processGooglePlayNotification(authorization: string, body: string) {
|
|
318
|
+
await verifyIdToken(authorization.replace('Bearer ', '')); // your OIDC check
|
|
319
|
+
|
|
320
|
+
const { message } = JSON.parse(body) as { message: PubsubMessage };
|
|
321
|
+
const notification = JSON.parse(
|
|
322
|
+
Buffer.from(message.data, 'base64').toString('utf-8')
|
|
323
|
+
) as Partial<DeveloperNotification>;
|
|
324
|
+
|
|
325
|
+
if (notification.subscriptionNotification) {
|
|
326
|
+
const { purchaseToken } = notification.subscriptionNotification;
|
|
327
|
+
const { data } = await publisher.purchases.subscriptionsv2.get({
|
|
328
|
+
packageName: config.googlePlay.package,
|
|
329
|
+
token: purchaseToken,
|
|
330
|
+
});
|
|
331
|
+
const status = mapSubscriptionStatus(data.subscriptionState as GooglePlaySubscriptionState);
|
|
332
|
+
const cancellationDetails = mapCancellationDetails(data.canceledStateContext);
|
|
333
|
+
const canceledAt = mapCanceledAt(data.canceledStateContext);
|
|
334
|
+
// save status, cancellationDetails, canceledAt ...
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Resolve the plan from a line item with `config.googlePlay.getPlan(productId, basePlanId)` and
|
|
340
|
+
`getBillingPeriod(productId, basePlanId)`. `GOOGLE_PLAY_SUBSCRIPTION_STATES` lists the raw
|
|
341
|
+
`SUBSCRIPTION_STATE_*` values.
|
|
@@ -20,7 +20,7 @@ type Options = {
|
|
|
20
20
|
appId: `${number}`;
|
|
21
21
|
bundleId: string;
|
|
22
22
|
};
|
|
23
|
-
declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
23
|
+
export declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
24
24
|
readonly appId: string;
|
|
25
25
|
readonly bundleId: string;
|
|
26
26
|
private readonly products;
|
|
@@ -45,5 +45,5 @@ declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, P
|
|
|
45
45
|
getCreditExpiresAt: (productId: string) => string;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
|
-
export {
|
|
48
|
+
export {};
|
|
49
49
|
//# sourceMappingURL=config.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;qBAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;mBAEQ;mBACA;mBACA;mBACA;MAEb;GAMH,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI,IAAI;UAWnD;SASA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,eAAA,MAAA,MAAA;;EAMlB,eAAgB,UAAU,IAAI,UAAU,IAAE,MAClC,GAAC,kBACW,KAAK,YAAY,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,QAAA,KAAA,KAAA;EAMnD,aAAc,aAAa,MAAM,qBAAmB,WACvC,UAAU,aAAa,GAAC,WACzB,aAKK,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;EAGrD,gBAAiB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAI1E,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;;EAIrD,sBAAmB,MAAU,IAAE,eAAiB;EAUhD,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAQP,kBAAe;mBAOE;EAMjB,qBAAkB"}
|
|
@@ -20,7 +20,7 @@ type Options = {
|
|
|
20
20
|
appId: `${number}`;
|
|
21
21
|
bundleId: string;
|
|
22
22
|
};
|
|
23
|
-
declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
23
|
+
export declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
24
24
|
readonly appId: string;
|
|
25
25
|
readonly bundleId: string;
|
|
26
26
|
private readonly products;
|
|
@@ -45,5 +45,5 @@ declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, P
|
|
|
45
45
|
getCreditExpiresAt: (productId: string) => string;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
|
-
export {
|
|
48
|
+
export {};
|
|
49
49
|
//# sourceMappingURL=config.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;qBAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;mBAEQ;mBACA;mBACA;mBACA;MAEb;GAMH,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI,IAAI;UAWnD;SASA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,eAAA,MAAA,MAAA;;EAMlB,eAAgB,UAAU,IAAI,UAAU,IAAE,MAClC,GAAC,kBACW,KAAK,YAAY,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,QAAA,KAAA,KAAA;EAMnD,aAAc,aAAa,MAAM,qBAAmB,WACvC,UAAU,aAAa,GAAC,WACzB,aAKK,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;EAGrD,gBAAiB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAI1E,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;;EAIrD,sBAAmB,MAAU,IAAE,eAAiB;EAUhD,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAQP,kBAAe;mBAOE;EAMjB,qBAAkB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JWSRenewalInfoDecodedPayload, JWSTransactionDecodedPayload, NotificationTypeV2, ResponseBodyV2DecodedPayload } from "@apple/app-store-server-library";
|
|
2
2
|
//#region src/app-store/handler.d.ts
|
|
3
|
-
interface AppStoreNotificationEvent {
|
|
3
|
+
export interface AppStoreNotificationEvent {
|
|
4
4
|
payload: ResponseBodyV2DecodedPayload;
|
|
5
5
|
transaction: JWSTransactionDecodedPayload | null;
|
|
6
6
|
renewalInfo: JWSRenewalInfoDecodedPayload | null;
|
|
@@ -12,7 +12,7 @@ type NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>
|
|
|
12
12
|
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
13
13
|
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
14
14
|
*/
|
|
15
|
-
declare class AppStoreNotificationHandler {
|
|
15
|
+
export declare class AppStoreNotificationHandler {
|
|
16
16
|
private readonly handlers;
|
|
17
17
|
private constructor();
|
|
18
18
|
static create(): AppStoreNotificationHandler;
|
|
@@ -20,5 +20,5 @@ declare class AppStoreNotificationHandler {
|
|
|
20
20
|
process: (event: AppStoreNotificationEvent) => Promise<void>;
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
|
23
|
-
export {
|
|
23
|
+
export {};
|
|
24
24
|
//# sourceMappingURL=handler.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.cts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"handler.d.cts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;iBAOiB;EACf,SAAS;EACT,aAAa;EACb,aAAa;;KAGV,yBAAyB,OAAO,8BAA8B;;;;;;;qBAQtD;mBACM;UAEV;SAIA,UAAM;EAIb,KAAE,MAAU,oBAAkB,SAAW;EASzC,UAAO,OAAiB,8BAA4B"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JWSRenewalInfoDecodedPayload, JWSTransactionDecodedPayload, NotificationTypeV2, ResponseBodyV2DecodedPayload } from "@apple/app-store-server-library";
|
|
2
2
|
//#region src/app-store/handler.d.ts
|
|
3
|
-
interface AppStoreNotificationEvent {
|
|
3
|
+
export interface AppStoreNotificationEvent {
|
|
4
4
|
payload: ResponseBodyV2DecodedPayload;
|
|
5
5
|
transaction: JWSTransactionDecodedPayload | null;
|
|
6
6
|
renewalInfo: JWSRenewalInfoDecodedPayload | null;
|
|
@@ -12,7 +12,7 @@ type NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>
|
|
|
12
12
|
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
13
13
|
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
14
14
|
*/
|
|
15
|
-
declare class AppStoreNotificationHandler {
|
|
15
|
+
export declare class AppStoreNotificationHandler {
|
|
16
16
|
private readonly handlers;
|
|
17
17
|
private constructor();
|
|
18
18
|
static create(): AppStoreNotificationHandler;
|
|
@@ -20,5 +20,5 @@ declare class AppStoreNotificationHandler {
|
|
|
20
20
|
process: (event: AppStoreNotificationEvent) => Promise<void>;
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
|
23
|
-
export {
|
|
23
|
+
export {};
|
|
24
24
|
//# sourceMappingURL=handler.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.mts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"handler.d.mts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;iBAOiB;EACf,SAAS;EACT,aAAa;EACb,aAAa;;KAGV,yBAAyB,OAAO,8BAA8B;;;;;;;qBAQtD;mBACM;UAEV;SAIA,UAAM;EAIb,KAAE,MAAU,oBAAkB,SAAW;EASzC,UAAO,OAAiB,8BAA4B"}
|
|
@@ -6,7 +6,7 @@ import { CancellationDetails } from "../subscription/index.cjs";
|
|
|
6
6
|
* expired. Strings and numbers as Apple sends them, so this needs no runtime import of the
|
|
7
7
|
* App Store Server Library.
|
|
8
8
|
*/
|
|
9
|
-
interface AppStoreCancellationSource {
|
|
9
|
+
export interface AppStoreCancellationSource {
|
|
10
10
|
notificationType: string;
|
|
11
11
|
subtype?: string | null;
|
|
12
12
|
/** `JWSRenewalInfoDecodedPayload.expirationIntent`: 1 customer, 2 billing, 3 price increase, 4 unavailable, 5 other. */
|
|
@@ -19,7 +19,6 @@ interface AppStoreCancellationSource {
|
|
|
19
19
|
* ref: [notificationType](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)
|
|
20
20
|
* ref: [expirationIntent](https://developer.apple.com/documentation/appstoreserverapi/expirationintent)
|
|
21
21
|
*/
|
|
22
|
-
declare function mapCancellationDetails(source: AppStoreCancellationSource): CancellationDetails | null;
|
|
22
|
+
export declare function mapCancellationDetails(source: AppStoreCancellationSource): CancellationDetails | null;
|
|
23
23
|
//#endregion
|
|
24
|
-
export { AppStoreCancellationSource, mapCancellationDetails };
|
|
25
24
|
//# sourceMappingURL=mapper.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/app-store/mapper.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/app-store/mapper.ts"],"mappings":";;;;;;;;iBAQiB;EACf;EACA;;EAEA;;;;;;;;;wBAUc,uBACd,QAAQ,6BACP"}
|
|
@@ -6,7 +6,7 @@ import { CancellationDetails } from "../subscription/index.mjs";
|
|
|
6
6
|
* expired. Strings and numbers as Apple sends them, so this needs no runtime import of the
|
|
7
7
|
* App Store Server Library.
|
|
8
8
|
*/
|
|
9
|
-
interface AppStoreCancellationSource {
|
|
9
|
+
export interface AppStoreCancellationSource {
|
|
10
10
|
notificationType: string;
|
|
11
11
|
subtype?: string | null;
|
|
12
12
|
/** `JWSRenewalInfoDecodedPayload.expirationIntent`: 1 customer, 2 billing, 3 price increase, 4 unavailable, 5 other. */
|
|
@@ -19,7 +19,6 @@ interface AppStoreCancellationSource {
|
|
|
19
19
|
* ref: [notificationType](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)
|
|
20
20
|
* ref: [expirationIntent](https://developer.apple.com/documentation/appstoreserverapi/expirationintent)
|
|
21
21
|
*/
|
|
22
|
-
declare function mapCancellationDetails(source: AppStoreCancellationSource): CancellationDetails | null;
|
|
22
|
+
export declare function mapCancellationDetails(source: AppStoreCancellationSource): CancellationDetails | null;
|
|
23
23
|
//#endregion
|
|
24
|
-
export { AppStoreCancellationSource, mapCancellationDetails };
|
|
25
24
|
//# sourceMappingURL=mapper.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/app-store/mapper.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/app-store/mapper.ts"],"mappings":";;;;;;;;iBAQiB;EACf;EACA;;EAEA;;;;;;;;;wBAUc,uBACd,QAAQ,6BACP"}
|
|
@@ -22,7 +22,7 @@ type Options = {
|
|
|
22
22
|
package: string;
|
|
23
23
|
audience: string;
|
|
24
24
|
};
|
|
25
|
-
declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
25
|
+
export declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
26
26
|
readonly package: string;
|
|
27
27
|
readonly audience: string;
|
|
28
28
|
private readonly onetimes;
|
|
@@ -44,5 +44,5 @@ declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>,
|
|
|
44
44
|
getCreditExpiresAt: (productId: string, planId?: string) => string;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
|
-
export {
|
|
47
|
+
export {};
|
|
48
48
|
//# sourceMappingURL=config.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;qBAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;mBACQ;mBACA;mBACA;GAIhB,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI;mBAcrC;UAIV;SAQA,SACL,aAAW,mBACX,8BACA,8BAA0B,SAEjB,YAAO,iBAAA,MAAA,MAAA;EAKlB,eAAgB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;EAI3F,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aAKK,iBAAiB,IAAI,IAAI,IAAI,KAAK;;EAInD,oBAAiB;EAMjB,UAAO,mBAAqB,mBAAmB;EAQ/C,mBAAgB,mBAAqB,mBAAmB;EAQxD,UAAO;EAMP,kBAAe,mBAAqB;mBAOnB;EAOjB,qBAAkB,mBAAqB"}
|
|
@@ -22,7 +22,7 @@ type Options = {
|
|
|
22
22
|
package: string;
|
|
23
23
|
audience: string;
|
|
24
24
|
};
|
|
25
|
-
declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
25
|
+
export declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
26
26
|
readonly package: string;
|
|
27
27
|
readonly audience: string;
|
|
28
28
|
private readonly onetimes;
|
|
@@ -44,5 +44,5 @@ declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>,
|
|
|
44
44
|
getCreditExpiresAt: (productId: string, planId?: string) => string;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
|
-
export {
|
|
47
|
+
export {};
|
|
48
48
|
//# sourceMappingURL=config.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;qBAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;mBACQ;mBACA;mBACA;GAIhB,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI;mBAcrC;UAIV;SAQA,SACL,aAAW,mBACX,8BACA,8BAA0B,SAEjB,YAAO,iBAAA,MAAA,MAAA;EAKlB,eAAgB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;EAI3F,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aAKK,iBAAiB,IAAI,IAAI,IAAI,KAAK;;EAInD,oBAAiB;EAMjB,UAAO,mBAAqB,mBAAmB;EAQ/C,mBAAgB,mBAAqB,mBAAmB;EAQxD,UAAO;EAMP,kBAAe,mBAAqB;mBAOnB;EAOjB,qBAAkB,mBAAqB"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CancellationDetails, SubscriptionStatus } from "../subscription/index.cjs";
|
|
2
2
|
import { GooglePlaySubscriptionState } from "./real-time-developer-notification.cjs";
|
|
3
3
|
//#region src/google-play/mapper.d.ts
|
|
4
|
-
declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
4
|
+
export declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
5
5
|
/**
|
|
6
6
|
* `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this
|
|
7
7
|
* module does not depend on the Play Developer API client.
|
|
8
8
|
*/
|
|
9
|
-
interface CanceledStateContext {
|
|
9
|
+
export interface CanceledStateContext {
|
|
10
10
|
userInitiatedCancellation?: {
|
|
11
11
|
cancelTime?: string | null;
|
|
12
12
|
cancelSurveyResult?: {
|
|
@@ -24,12 +24,11 @@ interface CanceledStateContext {
|
|
|
24
24
|
* the subscription renews, and for a replacement: that is the linked purchase taking over, not a
|
|
25
25
|
* cancellation to report.
|
|
26
26
|
*/
|
|
27
|
-
declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
27
|
+
export declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
28
28
|
/**
|
|
29
29
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
30
30
|
* the caller falls back to when it learnt of it.
|
|
31
31
|
*/
|
|
32
|
-
declare function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null;
|
|
32
|
+
export declare function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null;
|
|
33
33
|
//#endregion
|
|
34
|
-
export { CanceledStateContext, mapCanceledAt, mapCancellationDetails, mapSubscriptionStatus };
|
|
35
34
|
//# sourceMappingURL=mapper.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;wBAwBa,cAAc,SAAS"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CancellationDetails, SubscriptionStatus } from "../subscription/index.mjs";
|
|
2
2
|
import { GooglePlaySubscriptionState } from "./real-time-developer-notification.mjs";
|
|
3
3
|
//#region src/google-play/mapper.d.ts
|
|
4
|
-
declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
4
|
+
export declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
5
5
|
/**
|
|
6
6
|
* `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this
|
|
7
7
|
* module does not depend on the Play Developer API client.
|
|
8
8
|
*/
|
|
9
|
-
interface CanceledStateContext {
|
|
9
|
+
export interface CanceledStateContext {
|
|
10
10
|
userInitiatedCancellation?: {
|
|
11
11
|
cancelTime?: string | null;
|
|
12
12
|
cancelSurveyResult?: {
|
|
@@ -24,12 +24,11 @@ interface CanceledStateContext {
|
|
|
24
24
|
* the subscription renews, and for a replacement: that is the linked purchase taking over, not a
|
|
25
25
|
* cancellation to report.
|
|
26
26
|
*/
|
|
27
|
-
declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
27
|
+
export declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
28
28
|
/**
|
|
29
29
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
30
30
|
* the caller falls back to when it learnt of it.
|
|
31
31
|
*/
|
|
32
|
-
declare function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null;
|
|
32
|
+
export declare function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null;
|
|
33
33
|
//#endregion
|
|
34
|
-
export { CanceledStateContext, mapCanceledAt, mapCancellationDetails, mapSubscriptionStatus };
|
|
35
34
|
//# sourceMappingURL=mapper.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;wBAwBa,cAAc,SAAS"}
|