glitch-javascript-sdk 3.15.0 → 4.0.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/dist/browser/hosting-runtime.js +1 -1
- package/dist/browser/hosting-runtime.js.map +1 -1
- package/dist/cjs/index.js +7495 -20
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/api/Microtransactions.d.ts +256 -34
- package/dist/esm/index.js +7529 -20
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/util/Requests.d.ts +3 -1
- package/dist/index.d.ts +260 -36
- package/guides/commerce-delivery-receiver.mjs +117 -0
- package/guides/microtransactions.md +98 -17
- package/package.json +1 -1
- package/src/api/Microtransactions.ts +223 -33
- package/src/routes/MicrotransactionsRoute.ts +11 -0
- package/src/util/Requests.ts +2 -2
- package/src/util/Session.ts +3 -2
|
@@ -25,7 +25,9 @@ declare class Requests {
|
|
|
25
25
|
static put<T>(url: string, data: any, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
26
26
|
static patch<T>(url: string, data: any, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
27
27
|
static delete<T>(url: string, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
28
|
-
static uploadFile<T>(url: string, filename: string, file: File | Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, options?: Pick<AxiosRequestConfig, 'signal' | 'timeout'>
|
|
28
|
+
static uploadFile<T>(url: string, filename: string, file: File | Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, options?: Pick<AxiosRequestConfig, 'signal' | 'timeout'> & {
|
|
29
|
+
excludeCommunityContext?: boolean;
|
|
30
|
+
}): AxiosPromise<Response<T>>;
|
|
29
31
|
static postFormData<T>(url: string, formData: FormData, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void): AxiosPromise<Response<T>>;
|
|
30
32
|
static uploadBlob<T>(url: string, filename: string, blob: Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void): AxiosPromise<Response<T>>;
|
|
31
33
|
static uploadFileInChunks<T>(file: File, uploadUrl: string, onProgress?: (totalSize: number, amountUploaded: number) => void, data?: any, chunkSize?: number): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -12180,13 +12180,17 @@ declare class FestivalNetworking {
|
|
|
12180
12180
|
}
|
|
12181
12181
|
|
|
12182
12182
|
type MicrotransactionEnvironment = 'sandbox' | 'live';
|
|
12183
|
+
type MicrotransactionProviderName = 'stripe' | 'xsolla';
|
|
12183
12184
|
type MicrotransactionProductType = 'durable' | 'consumable' | 'currency' | 'bundle' | 'pass';
|
|
12184
12185
|
type MicrotransactionCurrency = 'USD' | 'EUR' | 'GBP' | 'CAD' | 'AUD' | 'JPY' | 'BRL' | 'INR' | 'KRW';
|
|
12185
12186
|
type MicrotransactionProductStatus = 'draft' | 'active' | 'archived';
|
|
12186
|
-
type MicrotransactionPaymentStatus = 'created' | 'action_required' | 'pending' | 'unknown' | 'paid' | 'failed' | 'canceled' | 'refund_pending' | 'partially_refunded' | 'refunded' | 'disputed' | 'quarantined';
|
|
12187
|
+
type MicrotransactionPaymentStatus = 'created' | 'action_required' | 'pending' | 'unknown' | 'paid' | 'failed' | 'canceled' | 'refund_pending' | 'partially_refunded' | 'refunded' | 'disputed' | 'quarantined' | 'refund_review';
|
|
12188
|
+
type MicrotransactionRefundStatus = 'requested' | 'linked' | 'unknown' | 'pending' | 'submitted' | 'succeeded' | 'failed' | 'canceled';
|
|
12189
|
+
type MicrotransactionDeliveryStatus = 'pending' | 'retrying' | 'processing' | 'acknowledged' | 'failed' | 'superseded';
|
|
12190
|
+
type MicrotransactionPayoutStatus = 'pending' | 'transferred' | 'bank_paid' | 'bank_pending' | 'bank_failed' | 'transfer_reversed';
|
|
12187
12191
|
type MicrotransactionFulfillmentStatus = 'not_ready' | 'pending' | 'delivered' | 'retrying' | 'failed' | 'revoked' | 'partially_recovered';
|
|
12188
12192
|
type MicrotransactionAbility = 'commerce:read' | 'commerce:write' | 'commerce:finance' | 'commerce:fulfill';
|
|
12189
|
-
type MicrotransactionErrorCode = 'authentication_required' | 'permission_denied' | '
|
|
12193
|
+
type MicrotransactionErrorCode = 'authentication_required' | 'permission_denied' | 'not_found' | 'not_eligible' | 'quote_expired' | 'already_owned' | 'idempotency_conflict' | 'payment_unknown' | 'rate_limited' | 'invalid_revenue_configuration' | 'fulfillment_pending' | 'provider_unavailable';
|
|
12190
12194
|
/** The backend's JSON envelope; Axios returns this envelope in response.data. */
|
|
12191
12195
|
interface MicrotransactionResponse<T> {
|
|
12192
12196
|
data: T;
|
|
@@ -12209,6 +12213,41 @@ interface MicrotransactionSessionOptions extends MicrotransactionRequestOptions
|
|
|
12209
12213
|
interface MicrotransactionEnvironmentFilter {
|
|
12210
12214
|
environment?: MicrotransactionEnvironment;
|
|
12211
12215
|
}
|
|
12216
|
+
/** Catalog discovery defaults to 200 records per page; absence on page one is not proof a SKU is unused. */
|
|
12217
|
+
interface MicrotransactionProductListFilters {
|
|
12218
|
+
page?: number;
|
|
12219
|
+
per_page?: number;
|
|
12220
|
+
status?: MicrotransactionProductStatus;
|
|
12221
|
+
/** Exact SKU, not a substring search. */
|
|
12222
|
+
sku?: string;
|
|
12223
|
+
}
|
|
12224
|
+
/** Administrative lists default to page 1 / 25 records and are scoped to the authorized title. */
|
|
12225
|
+
interface MicrotransactionManagementListFilters extends MicrotransactionEnvironmentFilter {
|
|
12226
|
+
page?: number;
|
|
12227
|
+
per_page?: number;
|
|
12228
|
+
status?: string;
|
|
12229
|
+
}
|
|
12230
|
+
interface MicrotransactionOrderListFilters extends MicrotransactionManagementListFilters {
|
|
12231
|
+
product_id?: string;
|
|
12232
|
+
status?: MicrotransactionPaymentStatus;
|
|
12233
|
+
payment_status?: MicrotransactionPaymentStatus;
|
|
12234
|
+
}
|
|
12235
|
+
interface MicrotransactionRelatedListFilters extends MicrotransactionManagementListFilters {
|
|
12236
|
+
order_id?: string;
|
|
12237
|
+
}
|
|
12238
|
+
interface MicrotransactionRefundListFilters extends MicrotransactionRelatedListFilters {
|
|
12239
|
+
status?: MicrotransactionRefundStatus;
|
|
12240
|
+
}
|
|
12241
|
+
interface MicrotransactionDeliveryListFilters extends MicrotransactionRelatedListFilters {
|
|
12242
|
+
status?: MicrotransactionDeliveryStatus;
|
|
12243
|
+
}
|
|
12244
|
+
interface MicrotransactionPayoutListFilters extends MicrotransactionRelatedListFilters {
|
|
12245
|
+
status?: MicrotransactionPayoutStatus;
|
|
12246
|
+
}
|
|
12247
|
+
/** @deprecated Optional compatibility field only; no confirmation or human-approval gate is enforced. */
|
|
12248
|
+
interface MicrotransactionLegacyConfirmation {
|
|
12249
|
+
confirm?: boolean;
|
|
12250
|
+
}
|
|
12212
12251
|
/** Self-only purchase-history filters. Identity comes from authentication, never a user_id argument. */
|
|
12213
12252
|
interface MicrotransactionMyPurchasesFilters extends MicrotransactionEnvironmentFilter {
|
|
12214
12253
|
/** Page number, integer 1–10000. Defaults to 1; ordering is created_at DESC, id DESC. */
|
|
@@ -12268,7 +12307,7 @@ interface MicrotransactionProductInput {
|
|
|
12268
12307
|
starts_at?: string | null;
|
|
12269
12308
|
ends_at?: string | null;
|
|
12270
12309
|
max_per_order?: number;
|
|
12271
|
-
/**
|
|
12310
|
+
/** @deprecated Ignored compatibility field. Title authorization and immutable-data validation remain required. */
|
|
12272
12311
|
confirm?: boolean;
|
|
12273
12312
|
}
|
|
12274
12313
|
interface MicrotransactionProduct extends Omit<MicrotransactionProductInput, 'confirm' | 'status' | 'media_ids'> {
|
|
@@ -12282,14 +12321,149 @@ interface MicrotransactionProduct extends Omit<MicrotransactionProductInput, 'co
|
|
|
12282
12321
|
updated_at: string;
|
|
12283
12322
|
}
|
|
12284
12323
|
interface MicrotransactionProvider {
|
|
12285
|
-
provider:
|
|
12324
|
+
provider: MicrotransactionProviderName;
|
|
12286
12325
|
environment: MicrotransactionEnvironment;
|
|
12287
12326
|
configured: boolean;
|
|
12288
|
-
|
|
12327
|
+
/** Actual external provider/account capability, not a manual approval flag. */
|
|
12328
|
+
available: boolean;
|
|
12329
|
+
enabled: boolean;
|
|
12330
|
+
priority: number;
|
|
12289
12331
|
countries: string[];
|
|
12290
12332
|
currencies: string[];
|
|
12333
|
+
minimum_amounts: Record<string, number>;
|
|
12291
12334
|
channels: string[];
|
|
12292
|
-
|
|
12335
|
+
payment_methods: string[];
|
|
12336
|
+
configuration: MicrotransactionProviderConfiguration;
|
|
12337
|
+
account: {
|
|
12338
|
+
id: string;
|
|
12339
|
+
country: string | null;
|
|
12340
|
+
charges_enabled: boolean;
|
|
12341
|
+
payouts_enabled: boolean;
|
|
12342
|
+
requirements_due: string[];
|
|
12343
|
+
} | null;
|
|
12344
|
+
/** The game's payout target. Do not substitute the platform processing account's payouts_enabled. */
|
|
12345
|
+
payout_account: {
|
|
12346
|
+
source: 'platform' | 'user' | 'community' | 'managed';
|
|
12347
|
+
id: string | null;
|
|
12348
|
+
available: boolean;
|
|
12349
|
+
country: string | null;
|
|
12350
|
+
transfers_active: boolean;
|
|
12351
|
+
payouts_enabled: boolean;
|
|
12352
|
+
requirements_due: string[];
|
|
12353
|
+
reasons: string[];
|
|
12354
|
+
};
|
|
12355
|
+
tax: {
|
|
12356
|
+
status: string;
|
|
12357
|
+
missing_fields: string[];
|
|
12358
|
+
};
|
|
12359
|
+
reasons: string[];
|
|
12360
|
+
checked_at: string | null;
|
|
12361
|
+
revision?: number;
|
|
12362
|
+
}
|
|
12363
|
+
interface MicrotransactionProviderSku {
|
|
12364
|
+
/** Provider SKU, 1–100 characters. */
|
|
12365
|
+
sku: string;
|
|
12366
|
+
currency: MicrotransactionCurrency;
|
|
12367
|
+
amount_minor: number;
|
|
12368
|
+
}
|
|
12369
|
+
interface MicrotransactionProviderConfiguration {
|
|
12370
|
+
tax_mode: 'automatic' | 'disabled';
|
|
12371
|
+
/** Stripe tax code txcd_ followed by exactly eight digits. */
|
|
12372
|
+
tax_code: string | null;
|
|
12373
|
+
payout_source: 'platform' | 'user' | 'community' | 'managed';
|
|
12374
|
+
/** Xsolla public project ID, 1–20 decimal digits. */
|
|
12375
|
+
project_id: string | null;
|
|
12376
|
+
/** Maximum 200 mappings. */
|
|
12377
|
+
sku_map: Record<string, MicrotransactionProviderSku>;
|
|
12378
|
+
}
|
|
12379
|
+
/** Developer preferences and an optional new owned Xsolla webhook secret only; never platform credentials, arbitrary payees, or availability facts. */
|
|
12380
|
+
interface MicrotransactionProviderInput extends Partial<MicrotransactionProviderConfiguration>, MicrotransactionLegacyConfirmation {
|
|
12381
|
+
environment: MicrotransactionEnvironment;
|
|
12382
|
+
enabled?: boolean;
|
|
12383
|
+
priority?: number;
|
|
12384
|
+
countries?: string[];
|
|
12385
|
+
currencies?: MicrotransactionCurrency[];
|
|
12386
|
+
minimum_amounts?: Record<string, number>;
|
|
12387
|
+
/** Write-only NEW owned Xsolla project secret, 16–512 chars, finance scope. Never a platform API key or MCP token; never returned/logged or put in game code. Existing platform/historical bindings cannot be overwritten. */
|
|
12388
|
+
webhook_secret?: string;
|
|
12389
|
+
}
|
|
12390
|
+
interface MicrotransactionProviderOnboardingInput extends MicrotransactionLegacyConfirmation {
|
|
12391
|
+
environment: MicrotransactionEnvironment;
|
|
12392
|
+
country: string;
|
|
12393
|
+
/** Stable caller-created key. Reuse with identical input after uncertain retries; never generate inside a retry. */
|
|
12394
|
+
idempotency_key: string;
|
|
12395
|
+
}
|
|
12396
|
+
interface MicrotransactionProviderOnboarding {
|
|
12397
|
+
title_id: string;
|
|
12398
|
+
provider: 'stripe';
|
|
12399
|
+
environment: MicrotransactionEnvironment;
|
|
12400
|
+
account_id: string;
|
|
12401
|
+
/** Single-use provider onboarding URL on connect.stripe.com; do not log or persist it. */
|
|
12402
|
+
onboarding_url: string;
|
|
12403
|
+
expires_at: string;
|
|
12404
|
+
status: 'requires_provider_onboarding';
|
|
12405
|
+
reused: boolean;
|
|
12406
|
+
}
|
|
12407
|
+
interface MicrotransactionDeliverySettings {
|
|
12408
|
+
title_id: string;
|
|
12409
|
+
environment: MicrotransactionEnvironment;
|
|
12410
|
+
enabled: boolean;
|
|
12411
|
+
url: string | null;
|
|
12412
|
+
signature_algorithm: 'ed25519' | 'hmac-sha256';
|
|
12413
|
+
/** Public verification material only. The private signing key never leaves the server. */
|
|
12414
|
+
verification_public_key: string | null;
|
|
12415
|
+
key_id: string | null;
|
|
12416
|
+
revision: number;
|
|
12417
|
+
configured: boolean;
|
|
12418
|
+
}
|
|
12419
|
+
interface MicrotransactionDeliverySettingsInput extends MicrotransactionLegacyConfirmation {
|
|
12420
|
+
environment: MicrotransactionEnvironment;
|
|
12421
|
+
enabled?: boolean;
|
|
12422
|
+
url?: string | null;
|
|
12423
|
+
}
|
|
12424
|
+
interface MicrotransactionDelivery {
|
|
12425
|
+
id: string;
|
|
12426
|
+
order_id: string;
|
|
12427
|
+
event_type: string;
|
|
12428
|
+
status: MicrotransactionDeliveryStatus;
|
|
12429
|
+
attempts: number;
|
|
12430
|
+
next_attempt_at: string | null;
|
|
12431
|
+
acknowledged_at: string | null;
|
|
12432
|
+
created_at: string;
|
|
12433
|
+
updated_at: string;
|
|
12434
|
+
}
|
|
12435
|
+
/** Replay/acknowledgement return only this safe subset, not the list's timestamps. */
|
|
12436
|
+
type MicrotransactionDeliveryResult = Pick<MicrotransactionDelivery, 'id' | 'order_id' | 'status' | 'event_type' | 'attempts' | 'acknowledged_at'>;
|
|
12437
|
+
interface MicrotransactionRefundRecord {
|
|
12438
|
+
id: string;
|
|
12439
|
+
order_id: string;
|
|
12440
|
+
status: MicrotransactionRefundStatus;
|
|
12441
|
+
amount_minor: number;
|
|
12442
|
+
reason: string;
|
|
12443
|
+
idempotency_key: string | null;
|
|
12444
|
+
record_type: 'request' | 'execution';
|
|
12445
|
+
execution_refund_id: string | null;
|
|
12446
|
+
execution_status: MicrotransactionRefundStatus | null;
|
|
12447
|
+
request_resolution: 'linked_to_execution' | 'not_executed' | null;
|
|
12448
|
+
order_refunded_minor: number | null;
|
|
12449
|
+
failure_code: string | null;
|
|
12450
|
+
created_at: string;
|
|
12451
|
+
updated_at: string;
|
|
12452
|
+
}
|
|
12453
|
+
interface MicrotransactionPayout {
|
|
12454
|
+
id: string;
|
|
12455
|
+
order_id: string;
|
|
12456
|
+
status: MicrotransactionPayoutStatus;
|
|
12457
|
+
amount_minor: number;
|
|
12458
|
+
provider_reference: string | null;
|
|
12459
|
+
created_at: string;
|
|
12460
|
+
updated_at: string;
|
|
12461
|
+
}
|
|
12462
|
+
interface MicrotransactionRefundInput extends MicrotransactionLegacyConfirmation {
|
|
12463
|
+
reason: string;
|
|
12464
|
+
amount_minor?: number;
|
|
12465
|
+
/** REQUIRED stable operation key, scoped to title/order. Reuse identical input on retry; changes conflict. */
|
|
12466
|
+
idempotency_key: string;
|
|
12293
12467
|
}
|
|
12294
12468
|
interface MicrotransactionReadiness {
|
|
12295
12469
|
status: 'disabled' | 'draft' | 'sandbox' | 'ready' | 'live' | 'degraded' | 'suspended';
|
|
@@ -12313,8 +12487,9 @@ interface MicrotransactionSettingsInput {
|
|
|
12313
12487
|
currencies?: MicrotransactionCurrency[];
|
|
12314
12488
|
branding?: Omit<MicrotransactionBranding, 'logo_media'>;
|
|
12315
12489
|
support_email?: string | null;
|
|
12490
|
+
/** @deprecated Legacy delivery alias requiring BOTH commerce:write and commerce:fulfill; prefer updateDeliverySettings/getDeliverySettings. */
|
|
12316
12491
|
webhook_url?: string | null;
|
|
12317
|
-
/**
|
|
12492
|
+
/** @deprecated Ignored compatibility field. Authorized title editors save directly; actual provider/sales restrictions remain. */
|
|
12318
12493
|
confirm?: boolean;
|
|
12319
12494
|
}
|
|
12320
12495
|
interface MicrotransactionSettings extends Omit<Required<MicrotransactionSettingsInput>, 'confirm'> {
|
|
@@ -12395,6 +12570,13 @@ interface MicrotransactionOrder {
|
|
|
12395
12570
|
items: MicrotransactionGrant[];
|
|
12396
12571
|
entitlements?: MicrotransactionEntitlement[];
|
|
12397
12572
|
}
|
|
12573
|
+
interface MicrotransactionOrderDetail extends MicrotransactionOrder {
|
|
12574
|
+
/** Optional, permission-scoped management relationships. Omission is not proof no records exist. */
|
|
12575
|
+
refunds?: Array<Pick<MicrotransactionRefundRecord, 'id' | 'order_id' | 'status'> & Partial<MicrotransactionRefundRecord>>;
|
|
12576
|
+
deliveries?: MicrotransactionDelivery[];
|
|
12577
|
+
payouts?: Array<Pick<MicrotransactionPayout, 'id' | 'order_id' | 'status'> & Partial<MicrotransactionPayout>>;
|
|
12578
|
+
financial_details_included?: boolean;
|
|
12579
|
+
}
|
|
12398
12580
|
type MicrotransactionGrantUsageStatus = 'unused' | 'partially_used' | 'used_up' | 'owned' | 'expired' | 'revoked' | 'not_delivered' | 'unavailable';
|
|
12399
12581
|
/** One purchase's server-calculated grant lot, not the player's aggregate inventory balance. */
|
|
12400
12582
|
interface MicrotransactionGrantUsage {
|
|
@@ -12545,8 +12727,10 @@ interface MicrotransactionConsumeInput {
|
|
|
12545
12727
|
}
|
|
12546
12728
|
interface MicrotransactionRefund {
|
|
12547
12729
|
refund_id: string;
|
|
12548
|
-
status:
|
|
12730
|
+
status: MicrotransactionRefundStatus;
|
|
12549
12731
|
order_id: string;
|
|
12732
|
+
idempotency_key: string;
|
|
12733
|
+
failure_code: string | null;
|
|
12550
12734
|
refund_allocation?: 'pro_rata_all_grants';
|
|
12551
12735
|
}
|
|
12552
12736
|
interface MicrotransactionRefundRequest {
|
|
@@ -12569,14 +12753,16 @@ interface MicrotransactionEarnings {
|
|
|
12569
12753
|
payouts_enabled: boolean;
|
|
12570
12754
|
reserve_days?: number;
|
|
12571
12755
|
}
|
|
12572
|
-
type MicrotransactionOperation = 'settings.get' | 'settings.update' | 'products.list' | 'products.create' | 'products.update' | 'products.archive' | 'providers.list' | 'readiness.get' | 'orders.list' | 'orders.get' | 'earnings.get' | 'refunds.request' | 'deliveries.replay' | 'integration.get' | 'integration.verify';
|
|
12756
|
+
type MicrotransactionOperation = 'settings.get' | 'settings.update' | 'products.list' | 'products.create' | 'products.update' | 'products.archive' | 'providers.list' | 'providers.update' | 'providers.refresh' | 'providers.onboarding' | 'readiness.get' | 'orders.list' | 'orders.get' | 'orders.reconcile' | 'earnings.get' | 'refunds.list' | 'refunds.get' | 'refunds.create' | 'refunds.request' | 'refunds.reconcile' | 'delivery.settings.get' | 'delivery.settings.update' | 'deliveries.list' | 'deliveries.replay' | 'deliveries.acknowledge' | 'payouts.list' | 'integration.get' | 'integration.verify';
|
|
12573
12757
|
interface MicrotransactionOperationCapability {
|
|
12574
12758
|
operation: MicrotransactionOperation;
|
|
12575
12759
|
description: string;
|
|
12576
12760
|
ability: MicrotransactionAbility;
|
|
12577
12761
|
input_schema: Record<string, unknown>;
|
|
12578
|
-
|
|
12579
|
-
|
|
12762
|
+
http_method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
12763
|
+
mutates: boolean;
|
|
12764
|
+
requires_confirmation: false;
|
|
12765
|
+
requires_human_approval: false;
|
|
12580
12766
|
examples: Array<Record<string, unknown>>;
|
|
12581
12767
|
output_description: string;
|
|
12582
12768
|
}
|
|
@@ -12608,42 +12794,78 @@ declare class Microtransactions {
|
|
|
12608
12794
|
static settings(title_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionSettings>>;
|
|
12609
12795
|
/** Atomic policy update. Sandbox/off by default. Cannot disable the final working revenue model. */
|
|
12610
12796
|
static updateSettings(title_id: string, data: MicrotransactionSettingsInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionSettings>>;
|
|
12611
|
-
/** Read-only country/provider
|
|
12797
|
+
/** Read-only current country/provider capability and revenue readiness; never fabricates availability. */
|
|
12612
12798
|
static readiness(title_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionReadiness>>;
|
|
12613
|
-
/**
|
|
12799
|
+
/** Paginated admin catalog including drafts/archives. Default 200, per_page 1–200/page 1–10000. Use exact sku to resolve uncertain creates. */
|
|
12800
|
+
static listProducts(title_id: string, params?: MicrotransactionProductListFilters, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12801
|
+
products: MicrotransactionProduct[];
|
|
12802
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12803
|
+
}>>;
|
|
12804
|
+
/** @deprecated Compatibility overload for the earlier second-argument request options. */
|
|
12614
12805
|
static listProducts(title_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12615
12806
|
products: MicrotransactionProduct[];
|
|
12807
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12616
12808
|
}>>;
|
|
12617
12809
|
/** Save a catalog product. Prices use integer minor units and attached media must belong to the title. */
|
|
12618
12810
|
static createProduct(title_id: string, data: MicrotransactionProductInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProduct>>;
|
|
12619
12811
|
/** Update a product version. Existing order snapshots remain unchanged. */
|
|
12620
12812
|
static updateProduct(title_id: string, product_id: string, data: Partial<MicrotransactionProductInput>, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProduct>>;
|
|
12621
|
-
/**
|
|
12622
|
-
static archiveProduct(title_id: string, product_id: string, data:
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12813
|
+
/** Direct authorized archive. Never deletes financial history or bypasses the last-revenue-model rule. */
|
|
12814
|
+
static archiveProduct(title_id: string, product_id: string, data?: MicrotransactionLegacyConfirmation, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProduct>>;
|
|
12815
|
+
/** Actual provider configuration/capability facts; no credentials or manual approval flag. */
|
|
12816
|
+
static providers(title_id: string, params?: MicrotransactionEnvironmentFilter, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12817
|
+
providers: MicrotransactionProvider[];
|
|
12818
|
+
}>>;
|
|
12819
|
+
/** @deprecated Compatibility overload for the earlier second-argument request options. */
|
|
12626
12820
|
static providers(title_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12627
12821
|
providers: MicrotransactionProvider[];
|
|
12628
12822
|
}>>;
|
|
12823
|
+
/** Direct commerce:finance configuration. Saving preferences does not fabricate external capability; inspect available/reasons. */
|
|
12824
|
+
static updateProvider(title_id: string, provider: MicrotransactionProviderName, data: MicrotransactionProviderInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProvider>>;
|
|
12825
|
+
/** Refresh authenticated external provider facts. May update cached state; never creates a payment or invents eligibility. */
|
|
12826
|
+
static refreshProvider(title_id: string, provider: MicrotransactionProviderName, data: {
|
|
12827
|
+
environment: MicrotransactionEnvironment;
|
|
12828
|
+
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProvider>>;
|
|
12829
|
+
/** Start/reuse owned Stripe Connect onboarding with one stable key. Provider KYC is factual setup, not a Glitch approval workflow. */
|
|
12830
|
+
static createProviderOnboarding(title_id: string, data: MicrotransactionProviderOnboardingInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionProviderOnboarding>>;
|
|
12831
|
+
/** Read title/environment delivery settings and the Ed25519 PUBLIC verification key. */
|
|
12832
|
+
static getDeliverySettings(title_id: string, params?: MicrotransactionEnvironmentFilter, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionDeliverySettings>>;
|
|
12833
|
+
/** Direct commerce:fulfill setup. Private/metadata network targets and private-key inputs remain forbidden. */
|
|
12834
|
+
static updateDeliverySettings(title_id: string, data: MicrotransactionDeliverySettingsInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionDeliverySettings>>;
|
|
12835
|
+
/** Discover safe event IDs/statuses before replay or acknowledge. Page 1–10000, per_page 1–100, default 25. */
|
|
12836
|
+
static listDeliveries(title_id: string, params?: MicrotransactionDeliveryListFilters, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12837
|
+
deliveries: MicrotransactionDelivery[];
|
|
12838
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12839
|
+
}>>;
|
|
12840
|
+
/** Financially scoped refund operation discovery; pending/unknown is not completed. */
|
|
12841
|
+
static listRefunds(title_id: string, params?: MicrotransactionRefundListFilters, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12842
|
+
refunds: MicrotransactionRefundRecord[];
|
|
12843
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12844
|
+
}>>;
|
|
12845
|
+
/** Inspect one same-title refund operation. */
|
|
12846
|
+
static getRefund(title_id: string, refund_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionRefundRecord>>;
|
|
12847
|
+
/** Query/retry the original persisted refund with its existing identity, never generate a new refund key. */
|
|
12848
|
+
static reconcileRefund(title_id: string, refund_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionRefundRecord>>;
|
|
12849
|
+
/** Discover provider transfer/payout records; transferred funds are not automatically a verified bank payout. */
|
|
12850
|
+
static listPayouts(title_id: string, params?: MicrotransactionPayoutListFilters, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12851
|
+
payouts: MicrotransactionPayout[];
|
|
12852
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12853
|
+
}>>;
|
|
12629
12854
|
/** Admin read of separate-currency balances; pending is not withdrawable revenue. */
|
|
12630
12855
|
static earnings(title_id: string, params?: MicrotransactionEnvironmentFilter, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionEarnings>>;
|
|
12631
|
-
/** Admin
|
|
12632
|
-
static listOrders(title_id: string, params?:
|
|
12856
|
+
/** Admin paginated redacted orders. Page 1–10000/per_page 1–100 (default 25); own-player history is separate. */
|
|
12857
|
+
static listOrders(title_id: string, params?: MicrotransactionOrderListFilters, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
12633
12858
|
orders: MicrotransactionOrder[];
|
|
12859
|
+
pagination: MicrotransactionPurchasePagination;
|
|
12634
12860
|
}>>;
|
|
12635
12861
|
/** Owner JWT/scoped player token or title admin. An arbitrary order UUID grants no access. */
|
|
12636
|
-
static getOrder(title_id: string, order_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<
|
|
12637
|
-
/**
|
|
12638
|
-
static
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
amount_minor?: number;
|
|
12642
|
-
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionRefund>>;
|
|
12862
|
+
static getOrder(title_id: string, order_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionOrderDetail>>;
|
|
12863
|
+
/** Financially scoped original-provider reconciliation. Does not reroute or start a different purchase. */
|
|
12864
|
+
static reconcileOrder(title_id: string, order_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionOrderDetail>>;
|
|
12865
|
+
/** Direct commerce:finance refund. REQUIRED stable idempotency_key; omission is an error, never auto-filled. Same-key changed input conflicts. */
|
|
12866
|
+
static refundOrder(title_id: string, order_id: string, data: MicrotransactionRefundInput, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionRefund>>;
|
|
12643
12867
|
/** Replay the same immutable event. Receiver must deduplicate event_id. This cannot mint goods. */
|
|
12644
|
-
static replayDelivery(title_id: string, delivery_id: string, data:
|
|
12645
|
-
confirm: true;
|
|
12646
|
-
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<Record<string, unknown>>>;
|
|
12868
|
+
static replayDelivery(title_id: string, delivery_id: string, data?: MicrotransactionLegacyConfirmation, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionDeliveryResult>>;
|
|
12647
12869
|
/** Public eligible catalog. Sandbox is restricted by backend environment/admin policy. */
|
|
12648
12870
|
static catalog(title_id: string, params?: MicrotransactionCatalogFilter, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionCatalog>>;
|
|
12649
12871
|
/** User-authenticated quote. Clients select product/quantity, never monetary values or seller accounts. */
|
|
@@ -12686,7 +12908,7 @@ declare class Microtransactions {
|
|
|
12686
12908
|
/** Record integration proof from a genuinely paid, fulfilled sandbox order with a claimed game handoff. */
|
|
12687
12909
|
static verifyIntegration(title_id: string, data: {
|
|
12688
12910
|
order_id: string;
|
|
12689
|
-
confirm
|
|
12911
|
+
confirm?: boolean;
|
|
12690
12912
|
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionReadiness>>;
|
|
12691
12913
|
/** Restore authoritative durable ownership/current consumable balances, never mutable cloud-save balances. */
|
|
12692
12914
|
static listEntitlements(title_id: string, params?: MicrotransactionEnvironmentFilter, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<{
|
|
@@ -12718,10 +12940,10 @@ declare class Microtransactions {
|
|
|
12718
12940
|
/** Trusted title server with commerce:fulfill or admin JWT acknowledges the immutable event. */
|
|
12719
12941
|
static acknowledgeDelivery(title_id: string, delivery_id: string, data: {
|
|
12720
12942
|
event_id: string;
|
|
12721
|
-
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<
|
|
12722
|
-
/** Title MCP token, never a runtime install token. Describes
|
|
12943
|
+
}, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionDeliveryResult>>;
|
|
12944
|
+
/** Title MCP token, never a runtime install token. Describes arguments, abilities, mutation semantics and provider facts. */
|
|
12723
12945
|
static mcpCapabilities(title_id: string, options?: MicrotransactionRequestOptions): AxiosPromise<MicrotransactionResponse<MicrotransactionCapabilities>>;
|
|
12724
|
-
/** Execute
|
|
12946
|
+
/** Execute a discovered authorized operation directly. Legacy confirm is ignored and not forwarded. */
|
|
12725
12947
|
static mcpOperation<T = Record<string, unknown>>(title_id: string, operation: MicrotransactionOperation, data: {
|
|
12726
12948
|
arguments: Record<string, unknown>;
|
|
12727
12949
|
confirm?: boolean;
|
|
@@ -12912,7 +13134,9 @@ declare class Requests {
|
|
|
12912
13134
|
static put<T>(url: string, data: any, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
12913
13135
|
static patch<T>(url: string, data: any, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
12914
13136
|
static delete<T>(url: string, params?: Record<string, any>): AxiosPromise<Response<T>>;
|
|
12915
|
-
static uploadFile<T>(url: string, filename: string, file: File | Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, options?: Pick<AxiosRequestConfig, 'signal' | 'timeout'>
|
|
13137
|
+
static uploadFile<T>(url: string, filename: string, file: File | Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, options?: Pick<AxiosRequestConfig, 'signal' | 'timeout'> & {
|
|
13138
|
+
excludeCommunityContext?: boolean;
|
|
13139
|
+
}): AxiosPromise<Response<T>>;
|
|
12916
13140
|
static postFormData<T>(url: string, formData: FormData, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void): AxiosPromise<Response<T>>;
|
|
12917
13141
|
static uploadBlob<T>(url: string, filename: string, blob: Blob, data?: any, params?: Record<string, any>, onUploadProgress?: (progressEvent: AxiosProgressEvent) => void): AxiosPromise<Response<T>>;
|
|
12918
13142
|
static uploadFileInChunks<T>(file: File, uploadUrl: string, onProgress?: (totalSize: number, amountUploaded: number) => void, data?: any, chunkSize?: number): Promise<void>;
|
|
@@ -13350,4 +13574,4 @@ declare class Glitch {
|
|
|
13350
13574
|
};
|
|
13351
13575
|
}
|
|
13352
13576
|
|
|
13353
|
-
export { type FestivalApplicationInput, type FestivalApplicationState, type FestivalConversation, type FestivalMediaUpload, type FestivalNetworkingFilters, type FestivalNetworkingProfile, type FestivalNetworkingResponse, type FestivalNetworkingSettings, type FestivalPost, type FestivalPostInput, type FestivalPostKind, type FestivalPostState, type FestivalPreferences, type FestivalReportInput, type FestivalRequestOptions, type FestivalWorkType, type MicrotransactionAbility, type MicrotransactionBranding, type MicrotransactionBridge, type MicrotransactionBridgeOptions, type MicrotransactionCapabilities, type MicrotransactionCatalog, type MicrotransactionCatalogFilter, type MicrotransactionCheckoutInput, type MicrotransactionCheckoutResult, type MicrotransactionCheckoutSession, type MicrotransactionCheckoutSessionInput, type MicrotransactionConsumeInput, type MicrotransactionCreatedCheckoutSession, type MicrotransactionCurrency, type MicrotransactionEarnings, type MicrotransactionEntitlement, type MicrotransactionEnvironment, type MicrotransactionEnvironmentFilter, type MicrotransactionError, type MicrotransactionErrorCode, type MicrotransactionFramePolicy, type MicrotransactionFulfillmentStatus, type MicrotransactionGrant, type MicrotransactionGrantUsage, type MicrotransactionGrantUsageStatus, type MicrotransactionHandoff, type MicrotransactionHandoffClaim, type MicrotransactionHandoffClaimInput, type MicrotransactionMedia, type MicrotransactionMyPurchases, type MicrotransactionMyPurchasesFilters, type MicrotransactionOperation, type MicrotransactionOperationCapability, type MicrotransactionOrder, type MicrotransactionOverlay, type MicrotransactionOverlayOptions, type MicrotransactionPaymentStatus, type MicrotransactionPlayerPurchase, type MicrotransactionPrice, type MicrotransactionProduct, type MicrotransactionProductInput, type MicrotransactionProductStatus, type MicrotransactionProductType, type MicrotransactionProvider, type MicrotransactionPurchaseInput, type MicrotransactionPurchaseMessage, type MicrotransactionPurchasePagination, type MicrotransactionQuote, type MicrotransactionReadiness, type MicrotransactionReadyMessage, type MicrotransactionRefund, type MicrotransactionRefundRequest, type MicrotransactionRequestOptions, type MicrotransactionResponse, type MicrotransactionRestoreBridgeOptions, type MicrotransactionSessionOptions, type MicrotransactionSettings, type MicrotransactionSettingsInput, type MicrotransactionVerifiedSession, createMicrotransactionBridge, createMicrotransactionNonce, createMicrotransactionRestoreBridge, Glitch as default, openMicrotransactionOverlay, openMicrotransactionRestoreOverlay };
|
|
13577
|
+
export { type FestivalApplicationInput, type FestivalApplicationState, type FestivalConversation, type FestivalMediaUpload, type FestivalNetworkingFilters, type FestivalNetworkingProfile, type FestivalNetworkingResponse, type FestivalNetworkingSettings, type FestivalPost, type FestivalPostInput, type FestivalPostKind, type FestivalPostState, type FestivalPreferences, type FestivalReportInput, type FestivalRequestOptions, type FestivalWorkType, type MicrotransactionAbility, type MicrotransactionBranding, type MicrotransactionBridge, type MicrotransactionBridgeOptions, type MicrotransactionCapabilities, type MicrotransactionCatalog, type MicrotransactionCatalogFilter, type MicrotransactionCheckoutInput, type MicrotransactionCheckoutResult, type MicrotransactionCheckoutSession, type MicrotransactionCheckoutSessionInput, type MicrotransactionConsumeInput, type MicrotransactionCreatedCheckoutSession, type MicrotransactionCurrency, type MicrotransactionDelivery, type MicrotransactionDeliveryListFilters, type MicrotransactionDeliveryResult, type MicrotransactionDeliverySettings, type MicrotransactionDeliverySettingsInput, type MicrotransactionDeliveryStatus, type MicrotransactionEarnings, type MicrotransactionEntitlement, type MicrotransactionEnvironment, type MicrotransactionEnvironmentFilter, type MicrotransactionError, type MicrotransactionErrorCode, type MicrotransactionFramePolicy, type MicrotransactionFulfillmentStatus, type MicrotransactionGrant, type MicrotransactionGrantUsage, type MicrotransactionGrantUsageStatus, type MicrotransactionHandoff, type MicrotransactionHandoffClaim, type MicrotransactionHandoffClaimInput, type MicrotransactionLegacyConfirmation, type MicrotransactionManagementListFilters, type MicrotransactionMedia, type MicrotransactionMyPurchases, type MicrotransactionMyPurchasesFilters, type MicrotransactionOperation, type MicrotransactionOperationCapability, type MicrotransactionOrder, type MicrotransactionOrderDetail, type MicrotransactionOrderListFilters, type MicrotransactionOverlay, type MicrotransactionOverlayOptions, type MicrotransactionPaymentStatus, type MicrotransactionPayout, type MicrotransactionPayoutListFilters, type MicrotransactionPayoutStatus, type MicrotransactionPlayerPurchase, type MicrotransactionPrice, type MicrotransactionProduct, type MicrotransactionProductInput, type MicrotransactionProductListFilters, type MicrotransactionProductStatus, type MicrotransactionProductType, type MicrotransactionProvider, type MicrotransactionProviderConfiguration, type MicrotransactionProviderInput, type MicrotransactionProviderName, type MicrotransactionProviderOnboarding, type MicrotransactionProviderOnboardingInput, type MicrotransactionProviderSku, type MicrotransactionPurchaseInput, type MicrotransactionPurchaseMessage, type MicrotransactionPurchasePagination, type MicrotransactionQuote, type MicrotransactionReadiness, type MicrotransactionReadyMessage, type MicrotransactionRefund, type MicrotransactionRefundInput, type MicrotransactionRefundListFilters, type MicrotransactionRefundRecord, type MicrotransactionRefundRequest, type MicrotransactionRefundStatus, type MicrotransactionRelatedListFilters, type MicrotransactionRequestOptions, type MicrotransactionResponse, type MicrotransactionRestoreBridgeOptions, type MicrotransactionSessionOptions, type MicrotransactionSettings, type MicrotransactionSettingsInput, type MicrotransactionVerifiedSession, createMicrotransactionBridge, createMicrotransactionNonce, createMicrotransactionRestoreBridge, Glitch as default, openMicrotransactionOverlay, openMicrotransactionRestoreOverlay };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node 24+ example: verify and durably queue Glitch Ed25519 notifications.
|
|
3
|
+
* Run behind your HTTPS reverse proxy. This is NOT an inventory grant engine:
|
|
4
|
+
* a worker/game session refreshes current entitlements with its own authorized
|
|
5
|
+
* player session after durable queue handling. Never apply payload snapshots.
|
|
6
|
+
*/
|
|
7
|
+
import { createPublicKey, verify } from 'node:crypto';
|
|
8
|
+
import { createServer } from 'node:http';
|
|
9
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
10
|
+
import { isAbsolute } from 'node:path';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const uuid = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
|
|
14
|
+
function reject(code, status = 400) { const error = new Error(code); error.code = code; error.status = status; throw error; }
|
|
15
|
+
function decodeBase64(value, length) {
|
|
16
|
+
if (typeof value !== 'string' || !/^[A-Za-z0-9+/]+={0,2}$/.test(value)) reject('invalid_base64');
|
|
17
|
+
const bytes = Buffer.from(value, 'base64');
|
|
18
|
+
if (bytes.length !== length || bytes.toString('base64') !== value) reject('invalid_base64');
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Configuration comes from authenticated delivery-settings, never the message. */
|
|
23
|
+
export function createDeliveryInbox({ titleId, environment, keyId, publicKeyBase64, databasePath }) {
|
|
24
|
+
if (!uuid.test(titleId) || !uuid.test(keyId) || !['sandbox', 'live'].includes(environment)) reject('invalid_receiver_configuration');
|
|
25
|
+
if (typeof databasePath !== 'string' || !isAbsolute(databasePath)) reject('durable_absolute_database_path_required');
|
|
26
|
+
// Ed25519 raw public keys need the standard SubjectPublicKeyInfo DER wrapper.
|
|
27
|
+
const publicKey = createPublicKey({ key: Buffer.concat([
|
|
28
|
+
Buffer.from('302a300506032b6570032100', 'hex'), decodeBase64(publicKeyBase64, 32)
|
|
29
|
+
]), format: 'der', type: 'spki' });
|
|
30
|
+
const db = new DatabaseSync(databasePath);
|
|
31
|
+
db.exec('PRAGMA busy_timeout=5000');
|
|
32
|
+
db.exec(`CREATE TABLE IF NOT EXISTS commerce_notifications (
|
|
33
|
+
title_id TEXT NOT NULL, environment TEXT NOT NULL, event_id TEXT NOT NULL,
|
|
34
|
+
order_id TEXT NOT NULL, event_type TEXT NOT NULL, order_version INTEGER NOT NULL,
|
|
35
|
+
received_at INTEGER NOT NULL, handled_at INTEGER,
|
|
36
|
+
PRIMARY KEY(title_id, environment, event_id)
|
|
37
|
+
)`);
|
|
38
|
+
const existing = db.prepare('SELECT order_id,event_type FROM commerce_notifications WHERE title_id=? AND environment=? AND event_id=?');
|
|
39
|
+
const insert = db.prepare('INSERT INTO commerce_notifications(title_id,environment,event_id,order_id,event_type,order_version,received_at) VALUES(?,?,?,?,?,?,?)');
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
/** rawBody must be the EXACT bytes received before any JSON parser. */
|
|
43
|
+
receive(headers, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
44
|
+
if (!Buffer.isBuffer(rawBody) || rawBody.length > 1048576) reject('invalid_body_size', 413);
|
|
45
|
+
const h = Object.fromEntries(Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]));
|
|
46
|
+
if (h['x-glitch-signature-algorithm'] !== 'ed25519' || h['x-glitch-key-id'] !== keyId) reject('unexpected_signing_key_or_algorithm', 401);
|
|
47
|
+
const timestamp = h['x-glitch-timestamp'];
|
|
48
|
+
if (typeof timestamp !== 'string' || !/^[0-9]{1,12}$/.test(timestamp) || Math.abs(nowSeconds - Number(timestamp)) > 300) reject('expired_timestamp', 401);
|
|
49
|
+
const signature = decodeBase64(h['x-glitch-signature'], 64);
|
|
50
|
+
const signed = Buffer.concat([Buffer.from(timestamp + '.', 'ascii'), rawBody]);
|
|
51
|
+
if (!verify(null, signed, publicKey, signature)) reject('invalid_signature', 401);
|
|
52
|
+
let event;
|
|
53
|
+
try { event = JSON.parse(rawBody.toString('utf8')); } catch { reject('invalid_json'); }
|
|
54
|
+
if (!event || event.title_id !== titleId || event.environment !== environment || !uuid.test(event.id || '')
|
|
55
|
+
|| event.id !== h['x-glitch-event-id'] || typeof event.type !== 'string'
|
|
56
|
+
|| !Number.isSafeInteger(event.order_version) || event.order_version < 0
|
|
57
|
+
|| !event.authoritative_order || !uuid.test(event.authoritative_order.id || '')
|
|
58
|
+
|| event.authoritative_order.title_id !== titleId || event.authoritative_order.environment !== environment) reject('event_scope_mismatch', 401);
|
|
59
|
+
const orderId = event.authoritative_order.id;
|
|
60
|
+
let duplicate = false;
|
|
61
|
+
db.exec('BEGIN IMMEDIATE');
|
|
62
|
+
try {
|
|
63
|
+
const prior = existing.get(titleId, environment, event.id);
|
|
64
|
+
if (prior) {
|
|
65
|
+
if (prior.order_id !== orderId || prior.event_type !== event.type) reject('event_identity_conflict', 409);
|
|
66
|
+
duplicate = true;
|
|
67
|
+
} else {
|
|
68
|
+
// Queue IDs only. Aggregate balances in an older signed notification
|
|
69
|
+
// are not safe to apply across out-of-order events from other orders.
|
|
70
|
+
insert.run(titleId, environment, event.id, orderId, event.type, event.order_version, nowSeconds);
|
|
71
|
+
}
|
|
72
|
+
db.exec('COMMIT');
|
|
73
|
+
} catch (error) { db.exec('ROLLBACK'); throw error; }
|
|
74
|
+
// Retry bodies may carry refreshed authoritative facts. The stable event
|
|
75
|
+
// identity is deduped; embedded inventory is NEVER reapplied on a retry.
|
|
76
|
+
return { event_id: event.id, duplicate };
|
|
77
|
+
},
|
|
78
|
+
close() { db.close(); },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Complete HTTP receiver. JSON/body middleware must not run before this handler. */
|
|
83
|
+
export function createDeliveryServer(inbox) {
|
|
84
|
+
return createServer(async (request, response) => {
|
|
85
|
+
if (request.method !== 'POST' || request.url !== '/glitch/commerce') { response.writeHead(404).end(); return; }
|
|
86
|
+
try {
|
|
87
|
+
const chunks = []; let size = 0;
|
|
88
|
+
for await (const chunk of request) {
|
|
89
|
+
size += chunk.length;
|
|
90
|
+
if (size > 1048576) reject('invalid_body_size', 413);
|
|
91
|
+
chunks.push(chunk);
|
|
92
|
+
}
|
|
93
|
+
const acknowledgement = inbox.receive(request.headers, Buffer.concat(chunks));
|
|
94
|
+
response.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
|
|
95
|
+
response.end(JSON.stringify(acknowledgement)); // Only after durable commit.
|
|
96
|
+
} catch (error) {
|
|
97
|
+
response.writeHead(error.status || 500, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
|
|
98
|
+
response.end(JSON.stringify({ error: error.code || 'delivery_handling_failed' }));
|
|
99
|
+
// Do not log raw payload, signatures, credentials or private player data.
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
105
|
+
process.umask(0o077); // Keep the new application inbox/journal private.
|
|
106
|
+
const inbox = createDeliveryInbox({
|
|
107
|
+
titleId: process.env.GLITCH_TITLE_ID,
|
|
108
|
+
environment: process.env.GLITCH_COMMERCE_ENVIRONMENT,
|
|
109
|
+
keyId: process.env.GLITCH_DELIVERY_KEY_ID,
|
|
110
|
+
publicKeyBase64: process.env.GLITCH_DELIVERY_PUBLIC_KEY,
|
|
111
|
+
databasePath: process.env.GAME_DELIVERY_DATABASE,
|
|
112
|
+
});
|
|
113
|
+
const server = createDeliveryServer(inbox);
|
|
114
|
+
server.listen(Number(process.env.PORT || 8787), '127.0.0.1');
|
|
115
|
+
const stop = () => server.close(() => { inbox.close(); process.exit(0); });
|
|
116
|
+
process.once('SIGINT', stop); process.once('SIGTERM', stop);
|
|
117
|
+
}
|