@onesub/server 0.18.0 → 0.18.2
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 +60 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/redis.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ app.use(createOneSubMiddleware({
|
|
|
44
44
|
store: new PostgresSubscriptionStore(process.env.DATABASE_URL),
|
|
45
45
|
purchaseStore: new PostgresPurchaseStore(process.env.DATABASE_URL),
|
|
46
46
|
// Optional:
|
|
47
|
-
adminSecret: process.env.ADMIN_SECRET, // enables
|
|
47
|
+
adminSecret: process.env.ADMIN_SECRET, // enables admin + metrics routes
|
|
48
48
|
logger: require('pino')(), // any { info, warn, error } logger
|
|
49
49
|
refundPolicy: 'immediate', // 'immediate' (default) | 'until_expiry'
|
|
50
50
|
}));
|
|
@@ -52,6 +52,33 @@ app.use(createOneSubMiddleware({
|
|
|
52
52
|
app.listen(4100);
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Serving multiple apps
|
|
56
|
+
|
|
57
|
+
Keep the top-level `apple`/`google` fields for a single app. To validate several bundle IDs and
|
|
58
|
+
package names from one process, configure `apps` and send `appId` in validation requests:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
app.use(createOneSubMiddleware({
|
|
62
|
+
database: { url: process.env.DATABASE_URL },
|
|
63
|
+
apps: [
|
|
64
|
+
{ id: 'coffee', apple: { bundleId: 'gg.pryzm.coffee' } },
|
|
65
|
+
{
|
|
66
|
+
id: 'penguinrun',
|
|
67
|
+
apple: { bundleId: 'gg.pryzm.penguinrun' },
|
|
68
|
+
google: {
|
|
69
|
+
packageName: 'gg.pryzm.penguinrun',
|
|
70
|
+
serviceAccountKey: process.env.PENGUINRUN_GOOGLE_SERVICE_ACCOUNT_KEY,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
defaultAppId: 'coffee',
|
|
75
|
+
}));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Apple receipts carry their bundle ID and can be resolved automatically. Google purchase tokens do
|
|
79
|
+
not carry a package name, so requests for a non-default Google app must include `appId`. Unknown
|
|
80
|
+
explicit app IDs fail closed and are never validated with another app's credentials.
|
|
81
|
+
|
|
55
82
|
## Endpoints
|
|
56
83
|
|
|
57
84
|
| Route | Purpose |
|
|
@@ -65,8 +92,19 @@ app.listen(4100);
|
|
|
65
92
|
| `DELETE /onesub/purchase/admin/:userId/:productId` | Wipe a non-consumable (requires `adminSecret`) |
|
|
66
93
|
| `POST /onesub/purchase/admin/grant` | Manually grant a purchase (requires `adminSecret`) |
|
|
67
94
|
| `POST /onesub/purchase/admin/transfer` | Reassign a `transactionId` to a new `userId` (requires `adminSecret`) |
|
|
95
|
+
| `GET /onesub/admin/subscriptions` | Filter and paginate subscriptions (requires `adminSecret`) |
|
|
96
|
+
| `GET /onesub/admin/subscriptions/:transactionId` | Get subscription detail (requires `adminSecret`) |
|
|
97
|
+
| `GET /onesub/admin/customers/:userId` | Get a customer profile (requires `adminSecret`) |
|
|
98
|
+
| `POST /onesub/admin/sync-apple/:originalTransactionId` | Refresh a subscription from Apple (requires `adminSecret`) |
|
|
68
99
|
| `GET /onesub/admin/webhook-deadletters` | List failed webhook jobs in DLQ (requires `adminSecret` + BullMQ) |
|
|
69
100
|
| `POST /onesub/admin/webhook-replay/:id` | Replay a dead-letter job (requires `adminSecret` + BullMQ) |
|
|
101
|
+
| `GET /onesub/entitlement?userId=&id=` | Evaluate one configured entitlement |
|
|
102
|
+
| `GET /onesub/entitlements?userId=` | Evaluate all configured entitlements |
|
|
103
|
+
| `GET /onesub/metrics/active` | Current entitled counts (requires `adminSecret`) |
|
|
104
|
+
| `GET /onesub/metrics/started` | Subscriptions started in a range (requires `adminSecret`) |
|
|
105
|
+
| `GET /onesub/metrics/expired` | Subscriptions ended in a range (requires `adminSecret`) |
|
|
106
|
+
| `GET /onesub/metrics/purchases/started` | Non-consumables started in a range (requires `adminSecret`) |
|
|
107
|
+
| `POST /onesub/apple/offer-signature` | Sign an Apple promotional offer when offer credentials are configured |
|
|
70
108
|
| `GET /openapi.json` | OpenAPI 3.1 spec (opt-in via `openapiHandler()`) |
|
|
71
109
|
|
|
72
110
|
## Lifecycle states (`0.4.0+`)
|
|
@@ -111,6 +149,20 @@ const sub = await fetchAppleSubscriptionStatus(originalTxId, config.apple, { san
|
|
|
111
149
|
// sub: SubscriptionInfo | null — null on missing creds / 404 / network failure
|
|
112
150
|
```
|
|
113
151
|
|
|
152
|
+
### Transaction History API client
|
|
153
|
+
|
|
154
|
+
`fetchAppleTransactionHistory()` follows Apple's revision pagination and returns verified transaction
|
|
155
|
+
records. It is a low-level recovery/migration primitive; the host decides how to reconcile returned
|
|
156
|
+
records with its stores.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { fetchAppleTransactionHistory } from '@onesub/server';
|
|
160
|
+
|
|
161
|
+
const transactions = await fetchAppleTransactionHistory(originalTxId, config.apple, {
|
|
162
|
+
sandbox: false,
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
114
166
|
### CONSUMPTION_REQUEST response hook
|
|
115
167
|
|
|
116
168
|
When Apple sends a `CONSUMPTION_REQUEST` notification (consumable refund review), without a hook Apple has no usage signal and tends to grant the refund. Provide a hook to PUT consumption info back to `/inApps/v1/transactions/consumption/{txId}`:
|
|
@@ -130,6 +182,13 @@ apple: {
|
|
|
130
182
|
|
|
131
183
|
Fire-and-forget; failures are logged but don't block the webhook 200.
|
|
132
184
|
|
|
185
|
+
### Promotional offer signing
|
|
186
|
+
|
|
187
|
+
Configure the separate subscription-offer key as `apple.offerKeyId` and `apple.offerPrivateKey` to
|
|
188
|
+
mount `POST /onesub/apple/offer-signature`. If `adminSecret` is configured, callers must send the
|
|
189
|
+
same value in `X-Onesub-Offer-Secret`. The endpoint returns StoreKit's `keyId`, `nonce`, `timestamp`,
|
|
190
|
+
and `signature`; never expose the private key to the client.
|
|
191
|
+
|
|
133
192
|
## Optional Google hooks (`0.8.0+`)
|
|
134
193
|
|
|
135
194
|
```ts
|