@hyodotdev/openiap-commerce-protocol 0.0.0-bootstrap.0 → 0.1.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/CONVENTION.md +168 -0
- package/DESIGN.md +1056 -0
- package/README.md +227 -5
- package/SPEC.md +1471 -0
- package/conformance/index.d.ts +303 -0
- package/conformance/index.mjs +2126 -0
- package/conformance/mock-provider.mjs +491 -0
- package/examples/entitlement-granted-no-subscription.json +12 -0
- package/examples/entitlement-revoked.json +21 -0
- package/examples/provider-capabilities.json +209 -0
- package/examples/store-event-mapping.json +287 -0
- package/examples/subscription-canceled.json +22 -0
- package/examples/subscription-product-changed.json +30 -0
- package/examples/subscription-renewed.json +29 -0
- package/examples/verify-purchase-request.json +6 -0
- package/examples/verify-purchase-result.json +7 -0
- package/generated/bindings/graphql-operations.json +87 -0
- package/generated/bindings/http-binding.json +143 -0
- package/generated/bindings/introspection-signature.json +320 -0
- package/generated/bindings/operations-sdl.json +4 -0
- package/generated/bindings/operations.graphql +366 -0
- package/generated/commerce-protocol.graphql +1219 -0
- package/generated/openapi/commerce-protocol.openapi.json +1413 -0
- package/generated/schemas/commerce-event.schema.json +499 -0
- package/generated/schemas/commerce-protocol.bundle.schema.json +1576 -0
- package/generated/schemas/operations.schema.json +578 -0
- package/generated/schemas/primitives.schema.json +101 -0
- package/generated/schemas/provider-capabilities.schema.json +205 -0
- package/generated/schemas/store-event-mapping.schema.json +211 -0
- package/generated/vectors/lifecycle.json +908 -0
- package/generated/vectors/operations.json +1122 -0
- package/package.json +62 -12
- package/schema/01-primitives.graphql +102 -0
- package/schema/02-commerce-event.graphql +195 -0
- package/schema/03-provider-capabilities.graphql +139 -0
- package/schema/04-store-event-mapping.graphql +98 -0
- package/schema/05-operations.graphql +461 -0
- package/schema/06-compiler-vocabulary.graphql +139 -0
- package/schema/07-protocol-metadata.graphql +76 -0
- package/src/index.d.ts +63 -0
- package/src/index.mjs +121 -0
- package/vectors/signatures.json +139 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Canonical full-selection GraphQL documents for every protocol operation. Generated from commerce-protocol.graphql — do not edit. The conformance runner sends exactly these documents; REST/GraphQL parity is judged on each `selection` tree, this protocol version's contract shape.",
|
|
3
|
+
"protocolVersion": "1.0",
|
|
4
|
+
"operations": {
|
|
5
|
+
"providerCapabilities": {
|
|
6
|
+
"kind": "query",
|
|
7
|
+
"document": "query ProviderCapabilities { providerCapabilities { specVersion implementation { name version } eventTypes stores profiles bindings } }",
|
|
8
|
+
"selection": {
|
|
9
|
+
"specVersion": true,
|
|
10
|
+
"implementation": {
|
|
11
|
+
"name": true,
|
|
12
|
+
"version": true
|
|
13
|
+
},
|
|
14
|
+
"eventTypes": true,
|
|
15
|
+
"stores": true,
|
|
16
|
+
"profiles": true,
|
|
17
|
+
"bindings": true
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"subscriptionStatus": {
|
|
21
|
+
"kind": "query",
|
|
22
|
+
"document": "query SubscriptionStatus($input: SubscriptionStatusInput!) { subscriptionStatus(input: $input) { active subscription { productId state active store expiresAt renewsAt willRenew cancellationReason startedAt updatedAt } } }",
|
|
23
|
+
"selection": {
|
|
24
|
+
"active": true,
|
|
25
|
+
"subscription": {
|
|
26
|
+
"productId": true,
|
|
27
|
+
"state": true,
|
|
28
|
+
"active": true,
|
|
29
|
+
"store": true,
|
|
30
|
+
"expiresAt": true,
|
|
31
|
+
"renewsAt": true,
|
|
32
|
+
"willRenew": true,
|
|
33
|
+
"cancellationReason": true,
|
|
34
|
+
"startedAt": true,
|
|
35
|
+
"updatedAt": true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"entitlements": {
|
|
40
|
+
"kind": "query",
|
|
41
|
+
"document": "query Entitlements($input: EntitlementsInput!) { entitlements(input: $input) { userId productIds subscriptions { productId state active store expiresAt renewsAt willRenew cancellationReason startedAt updatedAt } } }",
|
|
42
|
+
"selection": {
|
|
43
|
+
"userId": true,
|
|
44
|
+
"productIds": true,
|
|
45
|
+
"subscriptions": {
|
|
46
|
+
"productId": true,
|
|
47
|
+
"state": true,
|
|
48
|
+
"active": true,
|
|
49
|
+
"store": true,
|
|
50
|
+
"expiresAt": true,
|
|
51
|
+
"renewsAt": true,
|
|
52
|
+
"willRenew": true,
|
|
53
|
+
"cancellationReason": true,
|
|
54
|
+
"startedAt": true,
|
|
55
|
+
"updatedAt": true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"verifyPurchase": {
|
|
60
|
+
"kind": "mutation",
|
|
61
|
+
"document": "mutation VerifyPurchase($input: VerifyPurchaseInput!) { verifyPurchase(input: $input) { store isValid state productId environment } }",
|
|
62
|
+
"selection": {
|
|
63
|
+
"store": true,
|
|
64
|
+
"isValid": true,
|
|
65
|
+
"state": true,
|
|
66
|
+
"productId": true,
|
|
67
|
+
"environment": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"bindPurchase": {
|
|
71
|
+
"kind": "mutation",
|
|
72
|
+
"document": "mutation BindPurchase($input: BindPurchaseInput!) { bindPurchase(input: $input) { bound } }",
|
|
73
|
+
"selection": {
|
|
74
|
+
"bound": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"eraseUser": {
|
|
78
|
+
"kind": "mutation",
|
|
79
|
+
"document": "mutation EraseUser($input: EraseUserInput!) { eraseUser(input: $input) { accepted jobId status } }",
|
|
80
|
+
"selection": {
|
|
81
|
+
"accepted": true,
|
|
82
|
+
"jobId": true,
|
|
83
|
+
"status": true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "HTTP binding manifest for the OpenIAP Commerce Protocol operation surface. Generated from commerce-protocol.graphql — do not edit. Schema pointers resolve inside commerce-protocol.bundle.schema.json.",
|
|
3
|
+
"protocolVersion": "1.0",
|
|
4
|
+
"profiles": {
|
|
5
|
+
"verification": "1.0",
|
|
6
|
+
"entitlements": "1.0",
|
|
7
|
+
"events": "1.0",
|
|
8
|
+
"accountLifecycle": "1.0"
|
|
9
|
+
},
|
|
10
|
+
"bindings": {
|
|
11
|
+
"rest": "1.0",
|
|
12
|
+
"graphql": "1.0"
|
|
13
|
+
},
|
|
14
|
+
"errorStatus": {
|
|
15
|
+
"INVALID_REQUEST": 400,
|
|
16
|
+
"UNAUTHORIZED": 401,
|
|
17
|
+
"FORBIDDEN": 403,
|
|
18
|
+
"NOT_FOUND": 404,
|
|
19
|
+
"PURCHASE_NOT_FOUND": 404,
|
|
20
|
+
"CONFLICT": 409,
|
|
21
|
+
"UNSUPPORTED_STORE": 422,
|
|
22
|
+
"RATE_LIMITED": 429,
|
|
23
|
+
"INTERNAL_ERROR": 500,
|
|
24
|
+
"UNSUPPORTED_PROFILE": 501,
|
|
25
|
+
"VERIFICATION_FAILED": 502
|
|
26
|
+
},
|
|
27
|
+
"errorResponse": "#/$defs/ProtocolErrorResponse",
|
|
28
|
+
"operations": [
|
|
29
|
+
{
|
|
30
|
+
"name": "providerCapabilities",
|
|
31
|
+
"kind": "query",
|
|
32
|
+
"profile": "core",
|
|
33
|
+
"auth": "none",
|
|
34
|
+
"method": "GET",
|
|
35
|
+
"path": "/commerce/v1/capabilities",
|
|
36
|
+
"successStatus": 200,
|
|
37
|
+
"idempotent": true,
|
|
38
|
+
"errors": [
|
|
39
|
+
"RATE_LIMITED",
|
|
40
|
+
"INTERNAL_ERROR"
|
|
41
|
+
],
|
|
42
|
+
"input": null,
|
|
43
|
+
"result": "#/$defs/ProviderCapabilities"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "subscriptionStatus",
|
|
47
|
+
"kind": "query",
|
|
48
|
+
"profile": "entitlements",
|
|
49
|
+
"auth": "server",
|
|
50
|
+
"method": "GET",
|
|
51
|
+
"path": "/commerce/v1/subscriptions/status",
|
|
52
|
+
"successStatus": 200,
|
|
53
|
+
"idempotent": true,
|
|
54
|
+
"errors": [
|
|
55
|
+
"INVALID_REQUEST",
|
|
56
|
+
"UNAUTHORIZED",
|
|
57
|
+
"FORBIDDEN",
|
|
58
|
+
"RATE_LIMITED",
|
|
59
|
+
"INTERNAL_ERROR"
|
|
60
|
+
],
|
|
61
|
+
"input": "#/$defs/SubscriptionStatusInput",
|
|
62
|
+
"result": "#/$defs/SubscriptionStatusResult"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "entitlements",
|
|
66
|
+
"kind": "query",
|
|
67
|
+
"profile": "entitlements",
|
|
68
|
+
"auth": "server",
|
|
69
|
+
"method": "GET",
|
|
70
|
+
"path": "/commerce/v1/entitlements",
|
|
71
|
+
"successStatus": 200,
|
|
72
|
+
"idempotent": true,
|
|
73
|
+
"errors": [
|
|
74
|
+
"INVALID_REQUEST",
|
|
75
|
+
"UNAUTHORIZED",
|
|
76
|
+
"FORBIDDEN",
|
|
77
|
+
"RATE_LIMITED",
|
|
78
|
+
"INTERNAL_ERROR"
|
|
79
|
+
],
|
|
80
|
+
"input": "#/$defs/EntitlementsInput",
|
|
81
|
+
"result": "#/$defs/EntitlementsResult"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "verifyPurchase",
|
|
85
|
+
"kind": "mutation",
|
|
86
|
+
"profile": "verification",
|
|
87
|
+
"auth": "verification",
|
|
88
|
+
"method": "POST",
|
|
89
|
+
"path": "/commerce/v1/purchases/verify",
|
|
90
|
+
"successStatus": 200,
|
|
91
|
+
"idempotent": true,
|
|
92
|
+
"errors": [
|
|
93
|
+
"INVALID_REQUEST",
|
|
94
|
+
"UNAUTHORIZED",
|
|
95
|
+
"UNSUPPORTED_STORE",
|
|
96
|
+
"VERIFICATION_FAILED",
|
|
97
|
+
"RATE_LIMITED",
|
|
98
|
+
"INTERNAL_ERROR"
|
|
99
|
+
],
|
|
100
|
+
"input": "#/$defs/VerifyPurchaseInput",
|
|
101
|
+
"result": "#/$defs/VerifyPurchaseResult"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"name": "bindPurchase",
|
|
105
|
+
"kind": "mutation",
|
|
106
|
+
"profile": "accountLifecycle",
|
|
107
|
+
"auth": "server",
|
|
108
|
+
"method": "POST",
|
|
109
|
+
"path": "/commerce/v1/purchases/bind",
|
|
110
|
+
"successStatus": 200,
|
|
111
|
+
"idempotent": true,
|
|
112
|
+
"errors": [
|
|
113
|
+
"INVALID_REQUEST",
|
|
114
|
+
"UNAUTHORIZED",
|
|
115
|
+
"FORBIDDEN",
|
|
116
|
+
"UNSUPPORTED_STORE",
|
|
117
|
+
"RATE_LIMITED",
|
|
118
|
+
"INTERNAL_ERROR"
|
|
119
|
+
],
|
|
120
|
+
"input": "#/$defs/BindPurchaseInput",
|
|
121
|
+
"result": "#/$defs/BindPurchaseResult"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "eraseUser",
|
|
125
|
+
"kind": "mutation",
|
|
126
|
+
"profile": "accountLifecycle",
|
|
127
|
+
"auth": "server",
|
|
128
|
+
"method": "POST",
|
|
129
|
+
"path": "/commerce/v1/users/erase",
|
|
130
|
+
"successStatus": 202,
|
|
131
|
+
"idempotent": true,
|
|
132
|
+
"errors": [
|
|
133
|
+
"INVALID_REQUEST",
|
|
134
|
+
"UNAUTHORIZED",
|
|
135
|
+
"FORBIDDEN",
|
|
136
|
+
"RATE_LIMITED",
|
|
137
|
+
"INTERNAL_ERROR"
|
|
138
|
+
],
|
|
139
|
+
"input": "#/$defs/EraseUserInput",
|
|
140
|
+
"result": "#/$defs/EraseUserResult"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Structural introspection signature of the executable GraphQL projection. Generated from commerce-protocol.graphql — do not edit. The conformance runner compares a served schema's introspection against this as a subset: everything named here must exist with these exact kinds, types, nullability, arguments, and (for closed enums and objects) exact members. New types and members added to open objects are compatible MINOR extensions.",
|
|
3
|
+
"protocolVersion": "1.0",
|
|
4
|
+
"queryType": "Query",
|
|
5
|
+
"mutationType": "Mutation",
|
|
6
|
+
"types": {
|
|
7
|
+
"Store": {
|
|
8
|
+
"kind": "SCALAR"
|
|
9
|
+
},
|
|
10
|
+
"Environment": {
|
|
11
|
+
"kind": "SCALAR"
|
|
12
|
+
},
|
|
13
|
+
"Timestamp": {
|
|
14
|
+
"kind": "SCALAR"
|
|
15
|
+
},
|
|
16
|
+
"Identifier": {
|
|
17
|
+
"kind": "SCALAR"
|
|
18
|
+
},
|
|
19
|
+
"SubscriptionState": {
|
|
20
|
+
"kind": "ENUM",
|
|
21
|
+
"values": [
|
|
22
|
+
"Active",
|
|
23
|
+
"Expired",
|
|
24
|
+
"InBillingRetry",
|
|
25
|
+
"InGracePeriod",
|
|
26
|
+
"Paused",
|
|
27
|
+
"Refunded",
|
|
28
|
+
"Revoked",
|
|
29
|
+
"Unknown"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"StoreCapabilitiesMap": {
|
|
33
|
+
"kind": "SCALAR"
|
|
34
|
+
},
|
|
35
|
+
"ProviderCapabilities": {
|
|
36
|
+
"kind": "OBJECT",
|
|
37
|
+
"fields": {
|
|
38
|
+
"specVersion": {
|
|
39
|
+
"type": "MajorMinor!"
|
|
40
|
+
},
|
|
41
|
+
"implementation": {
|
|
42
|
+
"type": "Implementation"
|
|
43
|
+
},
|
|
44
|
+
"eventTypes": {
|
|
45
|
+
"type": "[NamespacedEventType!]!"
|
|
46
|
+
},
|
|
47
|
+
"stores": {
|
|
48
|
+
"type": "StoreCapabilitiesMap!"
|
|
49
|
+
},
|
|
50
|
+
"profiles": {
|
|
51
|
+
"type": "ProfileVersionMap"
|
|
52
|
+
},
|
|
53
|
+
"bindings": {
|
|
54
|
+
"type": "BindingVersionMap"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"ProfileVersionMap": {
|
|
59
|
+
"kind": "SCALAR"
|
|
60
|
+
},
|
|
61
|
+
"BindingVersionMap": {
|
|
62
|
+
"kind": "SCALAR"
|
|
63
|
+
},
|
|
64
|
+
"Implementation": {
|
|
65
|
+
"kind": "OBJECT",
|
|
66
|
+
"fields": {
|
|
67
|
+
"name": {
|
|
68
|
+
"type": "NonEmptyString"
|
|
69
|
+
},
|
|
70
|
+
"version": {
|
|
71
|
+
"type": "NonEmptyString"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"PurchaseVerificationState": {
|
|
76
|
+
"kind": "SCALAR"
|
|
77
|
+
},
|
|
78
|
+
"ErasureJobStatus": {
|
|
79
|
+
"kind": "SCALAR"
|
|
80
|
+
},
|
|
81
|
+
"AppleJws": {
|
|
82
|
+
"kind": "SCALAR"
|
|
83
|
+
},
|
|
84
|
+
"GooglePurchaseToken": {
|
|
85
|
+
"kind": "SCALAR"
|
|
86
|
+
},
|
|
87
|
+
"StoreUserIdentity": {
|
|
88
|
+
"kind": "SCALAR"
|
|
89
|
+
},
|
|
90
|
+
"StoreSku": {
|
|
91
|
+
"kind": "SCALAR"
|
|
92
|
+
},
|
|
93
|
+
"StoreReceiptId": {
|
|
94
|
+
"kind": "SCALAR"
|
|
95
|
+
},
|
|
96
|
+
"AppleEvidence": {
|
|
97
|
+
"kind": "INPUT_OBJECT",
|
|
98
|
+
"inputFields": {
|
|
99
|
+
"jws": "AppleJws!"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"GoogleEvidence": {
|
|
103
|
+
"kind": "INPUT_OBJECT",
|
|
104
|
+
"inputFields": {
|
|
105
|
+
"purchaseToken": "GooglePurchaseToken!"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"HorizonEvidence": {
|
|
109
|
+
"kind": "INPUT_OBJECT",
|
|
110
|
+
"inputFields": {
|
|
111
|
+
"userId": "StoreUserIdentity!",
|
|
112
|
+
"sku": "StoreSku!"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"AmazonEvidence": {
|
|
116
|
+
"kind": "INPUT_OBJECT",
|
|
117
|
+
"inputFields": {
|
|
118
|
+
"userId": "StoreUserIdentity!",
|
|
119
|
+
"receiptId": "StoreReceiptId!",
|
|
120
|
+
"sandbox": "Boolean"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"VerifyPurchaseInput": {
|
|
124
|
+
"kind": "INPUT_OBJECT",
|
|
125
|
+
"inputFields": {
|
|
126
|
+
"store": "Store!",
|
|
127
|
+
"apple": "AppleEvidence",
|
|
128
|
+
"google": "GoogleEvidence",
|
|
129
|
+
"horizon": "HorizonEvidence",
|
|
130
|
+
"amazon": "AmazonEvidence"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"VerifyPurchaseResult": {
|
|
134
|
+
"kind": "OBJECT",
|
|
135
|
+
"fields": {
|
|
136
|
+
"store": {
|
|
137
|
+
"type": "Store!"
|
|
138
|
+
},
|
|
139
|
+
"isValid": {
|
|
140
|
+
"type": "Boolean!"
|
|
141
|
+
},
|
|
142
|
+
"state": {
|
|
143
|
+
"type": "PurchaseVerificationState!"
|
|
144
|
+
},
|
|
145
|
+
"productId": {
|
|
146
|
+
"type": "NonEmptyString"
|
|
147
|
+
},
|
|
148
|
+
"environment": {
|
|
149
|
+
"type": "Environment"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"BindPurchaseInput": {
|
|
154
|
+
"kind": "INPUT_OBJECT",
|
|
155
|
+
"inputFields": {
|
|
156
|
+
"userId": "Identifier!",
|
|
157
|
+
"store": "Store!",
|
|
158
|
+
"apple": "AppleEvidence",
|
|
159
|
+
"google": "GoogleEvidence",
|
|
160
|
+
"horizon": "HorizonEvidence",
|
|
161
|
+
"amazon": "AmazonEvidence"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"BindPurchaseResult": {
|
|
165
|
+
"kind": "OBJECT",
|
|
166
|
+
"fields": {
|
|
167
|
+
"bound": {
|
|
168
|
+
"type": "Boolean!"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"SubscriptionStatusInput": {
|
|
173
|
+
"kind": "INPUT_OBJECT",
|
|
174
|
+
"inputFields": {
|
|
175
|
+
"userId": "Identifier!"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"SubscriptionStatusSnapshot": {
|
|
179
|
+
"kind": "OBJECT",
|
|
180
|
+
"fields": {
|
|
181
|
+
"productId": {
|
|
182
|
+
"type": "NonEmptyString!"
|
|
183
|
+
},
|
|
184
|
+
"state": {
|
|
185
|
+
"type": "SubscriptionState!"
|
|
186
|
+
},
|
|
187
|
+
"active": {
|
|
188
|
+
"type": "Boolean!"
|
|
189
|
+
},
|
|
190
|
+
"store": {
|
|
191
|
+
"type": "Store"
|
|
192
|
+
},
|
|
193
|
+
"expiresAt": {
|
|
194
|
+
"type": "Timestamp"
|
|
195
|
+
},
|
|
196
|
+
"renewsAt": {
|
|
197
|
+
"type": "Timestamp"
|
|
198
|
+
},
|
|
199
|
+
"willRenew": {
|
|
200
|
+
"type": "Boolean"
|
|
201
|
+
},
|
|
202
|
+
"cancellationReason": {
|
|
203
|
+
"type": "NonEmptyString"
|
|
204
|
+
},
|
|
205
|
+
"startedAt": {
|
|
206
|
+
"type": "Timestamp"
|
|
207
|
+
},
|
|
208
|
+
"updatedAt": {
|
|
209
|
+
"type": "Timestamp"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"closed": true
|
|
213
|
+
},
|
|
214
|
+
"SubscriptionStatusResult": {
|
|
215
|
+
"kind": "OBJECT",
|
|
216
|
+
"fields": {
|
|
217
|
+
"active": {
|
|
218
|
+
"type": "Boolean!"
|
|
219
|
+
},
|
|
220
|
+
"subscription": {
|
|
221
|
+
"type": "SubscriptionStatusSnapshot"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"closed": true
|
|
225
|
+
},
|
|
226
|
+
"EntitlementsInput": {
|
|
227
|
+
"kind": "INPUT_OBJECT",
|
|
228
|
+
"inputFields": {
|
|
229
|
+
"userId": "Identifier!"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"EntitlementsResult": {
|
|
233
|
+
"kind": "OBJECT",
|
|
234
|
+
"fields": {
|
|
235
|
+
"userId": {
|
|
236
|
+
"type": "Identifier!"
|
|
237
|
+
},
|
|
238
|
+
"productIds": {
|
|
239
|
+
"type": "[NonEmptyString!]!"
|
|
240
|
+
},
|
|
241
|
+
"subscriptions": {
|
|
242
|
+
"type": "[SubscriptionStatusSnapshot!]!"
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"closed": true
|
|
246
|
+
},
|
|
247
|
+
"EraseUserInput": {
|
|
248
|
+
"kind": "INPUT_OBJECT",
|
|
249
|
+
"inputFields": {
|
|
250
|
+
"userId": "Identifier!"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"EraseUserResult": {
|
|
254
|
+
"kind": "OBJECT",
|
|
255
|
+
"fields": {
|
|
256
|
+
"accepted": {
|
|
257
|
+
"type": "Boolean!"
|
|
258
|
+
},
|
|
259
|
+
"jobId": {
|
|
260
|
+
"type": "Identifier"
|
|
261
|
+
},
|
|
262
|
+
"status": {
|
|
263
|
+
"type": "ErasureJobStatus"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"MajorMinor": {
|
|
268
|
+
"kind": "SCALAR"
|
|
269
|
+
},
|
|
270
|
+
"NonEmptyString": {
|
|
271
|
+
"kind": "SCALAR"
|
|
272
|
+
},
|
|
273
|
+
"NamespacedEventType": {
|
|
274
|
+
"kind": "SCALAR"
|
|
275
|
+
},
|
|
276
|
+
"Query": {
|
|
277
|
+
"kind": "OBJECT",
|
|
278
|
+
"fields": {
|
|
279
|
+
"providerCapabilities": {
|
|
280
|
+
"type": "ProviderCapabilities!"
|
|
281
|
+
},
|
|
282
|
+
"subscriptionStatus": {
|
|
283
|
+
"type": "SubscriptionStatusResult!",
|
|
284
|
+
"args": {
|
|
285
|
+
"input": "SubscriptionStatusInput!"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
"entitlements": {
|
|
289
|
+
"type": "EntitlementsResult!",
|
|
290
|
+
"args": {
|
|
291
|
+
"input": "EntitlementsInput!"
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"Mutation": {
|
|
297
|
+
"kind": "OBJECT",
|
|
298
|
+
"fields": {
|
|
299
|
+
"verifyPurchase": {
|
|
300
|
+
"type": "VerifyPurchaseResult!",
|
|
301
|
+
"args": {
|
|
302
|
+
"input": "VerifyPurchaseInput!"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"bindPurchase": {
|
|
306
|
+
"type": "BindPurchaseResult!",
|
|
307
|
+
"args": {
|
|
308
|
+
"input": "BindPurchaseInput!"
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"eraseUser": {
|
|
312
|
+
"type": "EraseUserResult!",
|
|
313
|
+
"args": {
|
|
314
|
+
"input": "EraseUserInput!"
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "The executable GraphQL projection from bindings/operations.graphql, wrapped as JSON for bundlers. Generated from commerce-protocol.graphql — do not edit.",
|
|
3
|
+
"sdl": "# Executable GraphQL projection of the OpenIAP Commerce Protocol 1.0\n# operation surface. Generated from commerce-protocol.graphql — do not edit.\n# A conforming GraphQL binding defines everything this projection\n# defines, exactly as defined; a compatible MINOR may extend it\n# additively. Introspection, where enabled, must agree with the schema\n# served.\n\n\"\"\"\nThe commerce platform that is authoritative for the purchase. This is a store, not a device platform: one device platform can host several stores. The value space is OPEN. The listed values are the stores this version names, but an implementation that observes commerce on another platform MUST be able to emit it, and a consumer MUST accept and preserve an unrecognised store opaquely rather than reject the event. Use a lowercase, stable, unambiguous token.\n\"\"\"\nscalar Store\n\n\"\"\"\nThe store environment the purchase belongs to. The value space is open: receivers MUST accept and preserve an unrecognised value opaquely and MUST NOT reject an event because of it.\n\"\"\"\nscalar Environment\n\n\"\"\"\nAn instant, as integer milliseconds since the Unix epoch, UTC. Every timestamp in this specification uses this encoding. The one exception is the transport signature timestamp, which is in seconds; see the webhook section of SPEC.md.\n\"\"\"\nscalar Timestamp\n\n\"\"\"\nAn opaque, implementation-assigned identifier. Consumers MUST treat it as an opaque string and MUST NOT parse structure out of it.\n\"\"\"\nscalar Identifier\n\n\"\"\"\nThe lifecycle state of a subscription. This is a state, not an event, and it does not by itself decide entitlement — see Entitlement in SPEC.md. These members are PascalCase while event types are lowercase-dotted; the inconsistency is inherited from the deployed 1.0 wire format and is recorded in CONVENTION.md rather than silently corrected, because changing it would break receivers already decoding it.\n\"\"\"\nenum SubscriptionState {\n Active\n InGracePeriod\n InBillingRetry\n Paused\n Expired\n Revoked\n Refunded\n Unknown\n}\n\nscalar StoreCapabilitiesMap\n\n\"\"\"\nWhat an implementation supports: the specification version it speaks, the event types it can emit, and what it can actually observe per store. This is the document a consumer, an operator, or a tool reads to determine compatibility without guessing and without reading prose. It contains no commerce data.\n\"\"\"\ntype ProviderCapabilities {\n \"\"\"\n OpenIAP Commerce Protocol version this declaration was written against, as MAJOR.MINOR.\n \"\"\"\n specVersion: MajorMinor!\n \"\"\"\n Free-form name and version of the backend making this declaration. Present so an operator can tell two backends apart; it carries no normative meaning.\n \"\"\"\n implementation: Implementation\n \"\"\"\n Event types this implementation can emit. A consumer uses it to know which lifecycle signals to expect; absence of a type here means this implementation never produces it, which is different from a type that simply has not occurred yet.\n \"\"\"\n eventTypes: [NamespacedEventType!]!\n \"\"\"\n Keyed by store, using the same open value space as the event envelope. An implementation declares only the stores it actually integrates.\n \"\"\"\n stores: StoreCapabilitiesMap!\n \"\"\"\n Operation profiles this implementation serves, keyed by profile name with the profile version as the value. This version names `verification`, `entitlements`, `events`, and `accountLifecycle`; the key space is open, so a consumer MUST ignore a profile it does not recognise. An implementation MUST declare only profiles it implements and passes conformance for, and MUST NOT declare a profile it partially implements. Absent on a descriptor from an events-only emitter that predates the operation surface.\n \"\"\"\n profiles: ProfileVersionMap\n \"\"\"\n Transport bindings this implementation serves for the declared profiles, keyed by binding name with the binding version as the value. This version names `rest` and `graphql`; the key space is open. Declaring a binding means every declared profile operation is reachable over it. An implementation MAY serve one binding only. Absent on a descriptor from an events-only emitter.\n \"\"\"\n bindings: BindingVersionMap\n}\n\nscalar ProfileVersionMap\n\nscalar BindingVersionMap\n\n\"\"\"\nFree-form name and version of the backend making this declaration. Present so an operator can tell two backends apart; it carries no normative meaning.\n\"\"\"\ntype Implementation {\n \"\"\"\n Name of the backend making this declaration, for an operator reading a descriptor without knowing where it came from.\n \"\"\"\n name: NonEmptyString\n \"\"\"\n Version of that backend, when it publishes one. Not a specification version — that is `specVersion`.\n \"\"\"\n version: NonEmptyString\n}\n\n\"\"\"\nThe verification verdict for one piece of purchase evidence, as a normalized token. This is the purchase-validation axis SPEC.md 2.3 warns about — a CANCELED verification verdict and a canceled-but-still-entitled subscription are different facts. The value space is OPEN: a caller MUST tolerate a token it does not know and gate on `isValid` alone.\n\"\"\"\nscalar PurchaseVerificationState\n\n\"\"\"\nThe lifecycle state of a user-erasure job. The value space is open; `completed` is the only terminal token this version names.\n\"\"\"\nscalar ErasureJobStatus\n\n\"\"\"\nA compact signed transaction JWS. Bounded so an oversized body cannot be forwarded to a store: an Apple transaction JWS is a few kilobytes.\n\"\"\"\nscalar AppleJws\n\n\"\"\"\nAn opaque store purchase token. Bounded to keep an oversized body off the store API.\n\"\"\"\nscalar GooglePurchaseToken\n\n\"\"\"\nA store-issued user identity, as the store's own API returns it. Distinct from the protocol's opaque `userId`.\n\"\"\"\nscalar StoreUserIdentity\n\n\"\"\"\nA store add-on SKU. Bounded to a predictable identifier length.\n\"\"\"\nscalar StoreSku\n\n\"\"\"\nA store receipt identifier. Bounded to keep an oversized body off the store API.\n\"\"\"\nscalar StoreReceiptId\n\n\"\"\"\nStore evidence for one Apple App Store purchase: the signed StoreKit 2 transaction JWS the app received from the store.\n\"\"\"\ninput AppleEvidence {\n \"\"\"\n The compact signed transaction JWS, verbatim as the store handed it to the app.\n \"\"\"\n jws: AppleJws!\n}\n\n\"\"\"\nStore evidence for one Google Play purchase: the opaque purchase token the store issued to the device.\n\"\"\"\ninput GoogleEvidence {\n \"\"\"\n The opaque purchase token, verbatim as the store issued it to the device.\n \"\"\"\n purchaseToken: GooglePurchaseToken!\n}\n\n\"\"\"\nStore evidence for one Meta Horizon (Quest) entitlement. Horizon issues no server-verifiable receipt, so verification identifies the entitlement by user and SKU; the provider holds the store credentials.\n\"\"\"\ninput HorizonEvidence {\n \"\"\"\n The store's own user identity for the entitlement holder — not the protocol's opaque `userId`.\n \"\"\"\n userId: StoreUserIdentity!\n \"\"\"\n The add-on SKU as configured in the store's developer dashboard.\n \"\"\"\n sku: StoreSku!\n}\n\n\"\"\"\nStore evidence for one Amazon Appstore purchase, as returned by the Amazon SDK. `sandbox` selects the RVS sandbox for App Tester receipts where the provider permits it.\n\"\"\"\ninput AmazonEvidence {\n \"\"\"\n The store's own user identity from the purchase response — not the protocol's opaque `userId`.\n \"\"\"\n userId: StoreUserIdentity!\n \"\"\"\n The receipt identifier from the purchase or purchase-updates response.\n \"\"\"\n receiptId: StoreReceiptId!\n \"\"\"\n Verify against the store's sandbox where the provider permits it. Omitted means production.\n \"\"\"\n sandbox: Boolean\n}\n\n\"\"\"\nOne purchase's store evidence, discriminated by `store`. The store space stays open: a provider that does not integrate the named store rejects the call with UNSUPPORTED_STORE rather than failing schema validation, so a future store is a MINOR evidence-member addition, not a breaking change. For each store this version names, the matching evidence member is required.\n\"\"\"\ninput VerifyPurchaseInput {\n store: Store!\n apple: AppleEvidence\n google: GoogleEvidence\n horizon: HorizonEvidence\n amazon: AmazonEvidence\n}\n\n\"\"\"\nThe verification verdict. `isValid` is the authoritative acceptance gate: a caller MUST read it and MUST NOT re-derive acceptance from `state`. A verdict is a statement about the evidence at verification time, never an account mutation — verification binds no user.\n\"\"\"\ntype VerifyPurchaseResult {\n store: Store!\n \"\"\"\n Whether the provider accepts the evidence. The single authoritative acceptance gate.\n \"\"\"\n isValid: Boolean!\n state: PurchaseVerificationState!\n \"\"\"\n The product the store verified, when its response exposes one. Never the caller's claim.\n \"\"\"\n productId: NonEmptyString\n \"\"\"\n The store environment the provider verified against, using the same open value space as the event envelope.\n \"\"\"\n environment: Environment\n}\n\n\"\"\"\nConnects verified store evidence to the adopter's own opaque user identity. Binding is a server-side account mutation: only the server role may call it, and possession of a purchase token is deliberately not proof of ownership — a provider MUST refuse to move an existing binding through this operation.\n\"\"\"\ninput BindPurchaseInput {\n userId: Identifier!\n store: Store!\n apple: AppleEvidence\n google: GoogleEvidence\n horizon: HorizonEvidence\n amazon: AmazonEvidence\n}\n\n\"\"\"\nWhether a binding now exists between this user and the evidenced purchase. `bound: false` covers every non-binding outcome — unknown evidence, evidence bound to a different user, or a store the provider cannot bind — without distinguishing them, so the operation cannot be used to probe whether someone else's purchase exists. Re-binding the same user to the same evidence is idempotent and reports `bound: true`.\n\"\"\"\ntype BindPurchaseResult {\n \"\"\"\n True when a binding between this user and the evidenced purchase now exists.\n \"\"\"\n bound: Boolean!\n}\n\n\"\"\"\nSelects the user whose subscription standing the caller's backend reads.\n\"\"\"\ninput SubscriptionStatusInput {\n \"\"\"\n The opaque app-scoped user identity the caller's backend selected, in the identity space shared by provider and caller.\n \"\"\"\n userId: Identifier!\n}\n\n\"\"\"\nOne subscription as the provider currently records it, shaped for a server-side read. It is tokenless by construction: this object is CLOSED, so the schema itself rejects any member beyond those listed — no purchase token, store transaction identity, signed receipt, or provider-internal record identifier can appear, and the response is safe to hold in a developer backend.\n\"\"\"\ntype SubscriptionStatusSnapshot {\n \"\"\"\n Store product identifier of the subscription.\n \"\"\"\n productId: NonEmptyString!\n state: SubscriptionState!\n \"\"\"\n The entitlement gate for this subscription, evaluated with the predicate in SPEC.md 2.3 at the provider's read time. A caller gates access on this member, never on `state`.\n \"\"\"\n active: Boolean!\n store: Store\n expiresAt: Timestamp\n renewsAt: Timestamp\n \"\"\"\n Whether the store will attempt another billing period, when the provider records it.\n \"\"\"\n willRenew: Boolean\n \"\"\"\n Why the subscription stopped renewing, as the same open normalized token space the event envelope uses. Advisory, never a billing fact.\n \"\"\"\n cancellationReason: NonEmptyString\n startedAt: Timestamp\n updatedAt: Timestamp\n}\n\n\"\"\"\nThe user's current subscription standing. `active` answers the gate for the user as a whole; `subscription` is the most relevant record — the current entitling subscription when one exists, otherwise the provider's most recent record as context. It is omitted when the provider has no record for this user. A provider that cannot enumerate the user's records completely MUST fail the operation instead of answering from a partial read.\n\"\"\"\ntype SubscriptionStatusResult {\n \"\"\"\n Whether the user is entitled to anything right now. The gate for simple access checks.\n \"\"\"\n active: Boolean!\n subscription: SubscriptionStatusSnapshot\n}\n\n\"\"\"\nSelects the user whose access decision the caller's backend reads.\n\"\"\"\ninput EntitlementsInput {\n \"\"\"\n The opaque app-scoped user identity the caller's backend selected, in the identity space shared by provider and caller.\n \"\"\"\n userId: Identifier!\n}\n\n\"\"\"\nEvery product the user may access right now, with the entitling subscription records. Unknown, expired, and ambiguous records contribute nothing: a product appears only when its gate is open at the provider's read time. A provider that cannot enumerate the user's records completely MUST fail the operation instead of answering from a partial read.\n\"\"\"\ntype EntitlementsResult {\n userId: Identifier!\n \"\"\"\n Every product the user may access right now, deduplicated.\n \"\"\"\n productIds: [NonEmptyString!]!\n \"\"\"\n The subscription records whose open gates produced `productIds`.\n \"\"\"\n subscriptions: [SubscriptionStatusSnapshot!]!\n}\n\n\"\"\"\nSelects the user identity to erase from the provider's records.\n\"\"\"\ninput EraseUserInput {\n \"\"\"\n The opaque app-scoped user identity to remove from the provider's subscription records and protocol event identity.\n \"\"\"\n userId: Identifier!\n}\n\n\"\"\"\nAcknowledgement of an erasure request. Erasure removes the user identity from the provider's own records and event store; it is asynchronous where the provider processes it as a job, and re-requesting the same user is idempotent — it reports the current job rather than failing. A provider CANNOT unsend events: copies already delivered to the caller's own systems are the caller's responsibility to erase.\n\"\"\"\ntype EraseUserResult {\n \"\"\"\n Whether the provider accepted the erasure request.\n \"\"\"\n accepted: Boolean!\n jobId: Identifier\n status: ErasureJobStatus\n}\n\nscalar MajorMinor\n\nscalar NonEmptyString\n\nscalar NamespacedEventType\n\ntype Query {\n \"\"\"\n What this provider implements: protocol version, profiles, bindings, and per-store capability. Public and free of commerce data, so a consumer or a conformance runner reads it without credentials and without any central registry.\n \"\"\"\n providerCapabilities: ProviderCapabilities!\n\n \"\"\"\n A developer backend reads one authenticated user's subscription standing. Server role only: a shipped app must not be able to walk arbitrary user identities, which is why the verification role is refused here.\n \"\"\"\n subscriptionStatus(input: SubscriptionStatusInput!): SubscriptionStatusResult!\n\n \"\"\"\n A developer backend reads one authenticated user's access decision. Server role only, and fail-close: anything the provider cannot classify as currently entitled is absent from the answer.\n \"\"\"\n entitlements(input: EntitlementsInput!): EntitlementsResult!\n}\n\ntype Mutation {\n \"\"\"\n Verifies store evidence and returns the verdict. The verification role suffices: this operation binds no user, reads no account, and repeating it with the same evidence is safe. VERIFICATION_FAILED means the provider could not obtain a verdict — it never stands in for the store rejecting the evidence, which is a successful result with `isValid: false`.\n \"\"\"\n verifyPurchase(input: VerifyPurchaseInput!): VerifyPurchaseResult!\n\n \"\"\"\n Binds a verified purchase to the adopter's opaque user identity. Server role only; retrying is idempotent.\n \"\"\"\n bindPurchase(input: BindPurchaseInput!): BindPurchaseResult!\n\n \"\"\"\n Removes a user identity from the provider's subscription records and protocol event identity. Server role only; re-requesting the same user is idempotent and reports the current job.\n \"\"\"\n eraseUser(input: EraseUserInput!): EraseUserResult!\n}\n"
|
|
4
|
+
}
|