@agent-cards/sdk 0.20.0 → 0.21.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/CHANGELOG.md +4 -0
- package/PREFLIGHT.md +4 -0
- package/README.md +153 -0
- package/dist/cdp.d.ts +2 -1
- package/dist/cdp.js +161 -31
- package/dist/client.d.ts +97 -1
- package/dist/client.js +172 -5
- package/dist/cybersource-flex.d.ts +183 -0
- package/dist/cybersource-flex.generated.d.ts +92 -0
- package/dist/cybersource-flex.generated.js +689 -0
- package/dist/cybersource-flex.js +275 -0
- package/dist/gmo.d.ts +63 -0
- package/dist/gmo.generated.d.ts +60 -0
- package/dist/gmo.generated.js +586 -0
- package/dist/gmo.js +119 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/lifecycle.d.ts +8 -3
- package/dist/lifecycle.js +6 -2
- package/dist/pre-claim.d.ts +12 -1
- package/dist/pre-claim.js +135 -9
- package/dist/preflight-catalog.json +116 -0
- package/dist/preflight-schemas.json +8 -0
- package/dist/preflight.generated.js +15 -1
- package/dist/prepared-processor.d.ts +4 -2
- package/dist/prepared-processor.js +12 -2
- package/dist/registry.d.ts +17 -7
- package/dist/stripe-checkout.generated.js +37 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// CyberSource Flex Microform v2 in the checkout SDK (design cybersource_flex, revised).
|
|
2
|
+
//
|
|
3
|
+
// The card field is CyberSource's own iframe, and the request it sends (a POST of a compact JWE
|
|
4
|
+
// to /flex/v2/tokens) is sealed under a capture context the merchant minted for this checkout.
|
|
5
|
+
// The paused body is ciphertext of the agent's placeholder card, so nothing in it can be swapped:
|
|
6
|
+
// the cardholder's device seals the real card itself under the same context and sends it to
|
|
7
|
+
// CyberSource, and the transient token that comes back answers the paused request. The context
|
|
8
|
+
// lives only in the field iframe's URL fragment, so this module reads it there, verifies it
|
|
9
|
+
// offline against the signing keys payment-core pins (no network), and hands it to authorize()
|
|
10
|
+
// beside the paused request. The API and the device verify it again.
|
|
11
|
+
//
|
|
12
|
+
// Recognition is not coverage: Microform v0.x and Flex API direct traffic post to the same URL.
|
|
13
|
+
// A POST pauses only when it comes from a Microform v2 card field of a reviewed version whose
|
|
14
|
+
// committed document URL this attachment read, sealed under the context that URL carries. A check
|
|
15
|
+
// (eCheck) Microform carries no card and continues untouched; everything else is refused before
|
|
16
|
+
// any authorization exists.
|
|
17
|
+
//
|
|
18
|
+
// The API serves the Flex recognizer only to an org Agentcard enabled for CyberSource Flex, so
|
|
19
|
+
// for every other org nothing here runs: the SDK never recognizes a Flex host and the field's
|
|
20
|
+
// request goes through untouched, as it did before this build.
|
|
21
|
+
import { assertCybersourceFlexTemplate, cybersourceFlexHostEnvironment, cybersourceFlexTemplateKid, isCybersourceFlexRequest, parseMicroformFragment, verifyCaptureContext, } from './cybersource-flex.generated.js';
|
|
22
|
+
/** The processor id, as the registry and the API name it. */
|
|
23
|
+
export const CYBERSOURCE_FLEX_PSP = 'cybersource_flex';
|
|
24
|
+
/**
|
|
25
|
+
* The dark-launch capability the Flex recognizer names: the capture-context sidecar a create
|
|
26
|
+
* must carry. syncRegistry declares it (SUPPORTED_CAPABILITIES), because this build reads the
|
|
27
|
+
* context and sends it; the API serves the recognizer only to a client that does.
|
|
28
|
+
*/
|
|
29
|
+
export const CYBERSOURCE_FLEX_CAPABILITY = 'capture_context';
|
|
30
|
+
const MICROFORM_FIELDS = ['number', 'securityCode'];
|
|
31
|
+
/** An https URL on flex.cybersource.com or testflex.cybersource.com, with no port or credentials. */
|
|
32
|
+
export function isCybersourceFlexHost(url) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(url);
|
|
35
|
+
return parsed.protocol === 'https:' && !parsed.port && !parsed.username && !parsed.password
|
|
36
|
+
&& cybersourceFlexHostEnvironment(parsed.hostname) !== null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* How an adapter fails a card request it will not let through: a CyberSource Flex request as a
|
|
44
|
+
* network error (Playwright 'failed', CDP 'Failed', net::ERR_FAILED), every other request as an
|
|
45
|
+
* abort (net::ERR_ABORTED). Microform 2.12.1's field reports a failed request to the merchant's page
|
|
46
|
+
* at once (createToken's callback gets CREATE_TOKEN_XHR_ERROR), so the page runs its own error path:
|
|
47
|
+
* a message, a retry under the same capture context, or GetPay's reload under a new one. It never
|
|
48
|
+
* reports an aborted request, so after a refusal or a decline the page's createToken would wait with
|
|
49
|
+
* no answer. Both were observed with the real field on CyberSource's sandbox.
|
|
50
|
+
*/
|
|
51
|
+
export function refusedRequestFailure(url) {
|
|
52
|
+
return isCybersourceFlexHost(url) ? 'failed' : 'aborted';
|
|
53
|
+
}
|
|
54
|
+
function frameOf(url) {
|
|
55
|
+
if (typeof url !== 'string' || !url)
|
|
56
|
+
return null;
|
|
57
|
+
try {
|
|
58
|
+
return parseMicroformFragment(url);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** payment-core's own clock allowance on a context's exp. */
|
|
65
|
+
const CLOCK_SKEW_MS = 60_000;
|
|
66
|
+
const MAX_CONTEXTS = 32;
|
|
67
|
+
const MAX_FRAMES = 64;
|
|
68
|
+
function causeOf(error) {
|
|
69
|
+
const reason = error?.reason;
|
|
70
|
+
return typeof reason === 'string' && /^[a-z_]{1,32}$/.test(reason) ? reason : 'crypto';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Each capture context verified once, offline, against the keys payment-core pins, and
|
|
74
|
+
* remembered for the attachment. The adapters start a context's check when its field iframe
|
|
75
|
+
* loads, so by the time the page sends its card the answer is usually settled and the pause
|
|
76
|
+
* stays synchronous. Bounded: the oldest context is dropped past 32.
|
|
77
|
+
*/
|
|
78
|
+
export class CaptureContexts {
|
|
79
|
+
entries = new Map();
|
|
80
|
+
verify(jwt) {
|
|
81
|
+
let entry = this.entries.get(jwt);
|
|
82
|
+
if (!entry) {
|
|
83
|
+
if (this.entries.size >= MAX_CONTEXTS)
|
|
84
|
+
this.entries.delete(this.entries.keys().next().value);
|
|
85
|
+
const created = { promise: Promise.resolve({ ok: false, cause: 'crypto' }) };
|
|
86
|
+
created.promise = Promise.resolve()
|
|
87
|
+
.then(() => verifyCaptureContext(jwt, { crypto: globalThis.crypto }))
|
|
88
|
+
.then((context) => (created.settled = { ok: true, context: context }), (error) => (created.settled = { ok: false, cause: causeOf(error) }));
|
|
89
|
+
this.entries.set(jwt, created);
|
|
90
|
+
entry = created;
|
|
91
|
+
}
|
|
92
|
+
return entry.settled ?? entry.promise;
|
|
93
|
+
}
|
|
94
|
+
/** Start the check of a card field iframe's capture context from the frame's URL, when it is one. */
|
|
95
|
+
warm(url) {
|
|
96
|
+
const frame = frameOf(url);
|
|
97
|
+
if (frame && frame.microformType === 'card' && frame.reviewed)
|
|
98
|
+
void this.verify(frame.captureContext);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The fields that loaded for the issuing frame's Microform: every frame sharing its capture
|
|
103
|
+
* context, microform id, version, host and type. Null when they are not a number field plus an
|
|
104
|
+
* optional security code field, which is all the device can rebuild.
|
|
105
|
+
*/
|
|
106
|
+
function fieldsOf(issuing, frames) {
|
|
107
|
+
const types = new Set([issuing.fieldType]);
|
|
108
|
+
for (const frame of frames) {
|
|
109
|
+
if (frame.captureContext === issuing.captureContext && frame.microformId === issuing.microformId
|
|
110
|
+
&& frame.version === issuing.version && frame.environment === issuing.environment
|
|
111
|
+
&& frame.microformType === issuing.microformType)
|
|
112
|
+
types.add(frame.fieldType);
|
|
113
|
+
}
|
|
114
|
+
if (!types.has('number') || [...types].some((type) => !MICROFORM_FIELDS.includes(type)))
|
|
115
|
+
return null;
|
|
116
|
+
return MICROFORM_FIELDS.filter((field) => types.has(field));
|
|
117
|
+
}
|
|
118
|
+
function afterVerification(request, issuing, fields, verification) {
|
|
119
|
+
if (!verification.ok)
|
|
120
|
+
return { kind: 'refuse', reason: 'capture_context_invalid', cause: verification.cause };
|
|
121
|
+
const context = verification.context;
|
|
122
|
+
if (!(context.exp * 1000 > Date.now() - CLOCK_SKEW_MS))
|
|
123
|
+
return { kind: 'refuse', reason: 'capture_context_invalid', cause: 'expired' };
|
|
124
|
+
if (context.url !== request.url || context.environment !== issuing.environment) {
|
|
125
|
+
return { kind: 'refuse', reason: 'capture_context_invalid', cause: 'url_mismatch' };
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
assertCybersourceFlexTemplate(request.body, context);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return { kind: 'refuse', reason: 'capture_context_mismatch' };
|
|
132
|
+
}
|
|
133
|
+
const trigger = issuing.fieldType;
|
|
134
|
+
return {
|
|
135
|
+
kind: 'pause',
|
|
136
|
+
flex: {
|
|
137
|
+
sidecar: { capture_context: issuing.captureContext, microform: { version: issuing.version, fields, trigger } },
|
|
138
|
+
shape: `jwe:${issuing.microformType}:${fields.join(',')}:${cybersourceFlexTemplateKid(request.body)}:${issuing.captureContext}`,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Judge one POST to a Flex host, given the frame that sent it and every Microform frame this
|
|
144
|
+
* attachment knows. `issuing` is that frame read as a Microform field: `undefined` when its URL
|
|
145
|
+
* could not be read at all, `null` when it was read and is no Microform v2 field (the merchant's
|
|
146
|
+
* own page or frame, which is where Flex API direct traffic comes from). In order:
|
|
147
|
+
* 1. a check (eCheck) Microform: continue, it carries no card;
|
|
148
|
+
* 2. a body the adapter could not read: refuse this request;
|
|
149
|
+
* 3. not a field iframe's sealed card (application/jwt, a Microform JWE, the exact tokens
|
|
150
|
+
* URL): unsupported, Microform v0.x and Flex API direct post here too;
|
|
151
|
+
* 4. a frame that could not be read: refuse this request, since the read can fail for a
|
|
152
|
+
* reason the page's next request does not repeat (a missed event, a slow frame tree read);
|
|
153
|
+
* 5. a frame read and found to be no Microform field, an unreviewed version, or fields the
|
|
154
|
+
* device cannot rebuild: unsupported, as the page's next request repeats each of them;
|
|
155
|
+
* 6. a capture context that does not verify, has expired, names another URL or host, or did
|
|
156
|
+
* not seal this body: refuse this request;
|
|
157
|
+
* 7. otherwise pause, with the sidecar the create carries.
|
|
158
|
+
* Returns synchronously when the context's verification already settled.
|
|
159
|
+
*/
|
|
160
|
+
export function judgeMicroformRequest(request, issuing, frames, contexts) {
|
|
161
|
+
if (issuing && issuing.microformType !== 'card')
|
|
162
|
+
return { kind: 'continue' };
|
|
163
|
+
if (request.body === null)
|
|
164
|
+
return { kind: 'refuse', reason: 'request_body_unreadable' };
|
|
165
|
+
if (!isCybersourceFlexRequest(request.url, 'POST', request.body, request.headers)) {
|
|
166
|
+
return { kind: 'unsupported', reason: 'microform_request_unrecognized' };
|
|
167
|
+
}
|
|
168
|
+
if (issuing === undefined)
|
|
169
|
+
return { kind: 'refuse', reason: 'microform_frame_unread' };
|
|
170
|
+
if (issuing === null)
|
|
171
|
+
return { kind: 'unsupported', reason: 'microform_request_unrecognized' };
|
|
172
|
+
if (!issuing.reviewed)
|
|
173
|
+
return { kind: 'unsupported', reason: 'microform_version_unreviewed' };
|
|
174
|
+
const fields = fieldsOf(issuing, frames);
|
|
175
|
+
if (!fields || !MICROFORM_FIELDS.includes(issuing.fieldType)) {
|
|
176
|
+
return { kind: 'unsupported', reason: 'microform_fields_unsupported' };
|
|
177
|
+
}
|
|
178
|
+
const withBody = request;
|
|
179
|
+
const verification = contexts.verify(issuing.captureContext);
|
|
180
|
+
return verification instanceof Promise
|
|
181
|
+
? verification.then((settled) => afterVerification(withBody, issuing, fields, settled))
|
|
182
|
+
: afterVerification(withBody, issuing, fields, verification);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The CDP adapter's record of Microform field iframes, keyed by frame id (an out-of-process
|
|
186
|
+
* iframe's target id is its frame id). Fed only from CDP sources that report a document the
|
|
187
|
+
* frame COMMITTED, with its fragment: Page.frameNavigated (the child's own session for an
|
|
188
|
+
* out-of-process iframe, the parent's for an in-process one), the iframe target's url on
|
|
189
|
+
* Target.attachedToTarget when the SDK attaches to a form that already rendered, and the
|
|
190
|
+
* pausing session's Page.getFrameTree as a last read. A document request (Network.
|
|
191
|
+
* requestWillBeSent, Fetch.requestPaused) is only a hint that warms the context's check: a page
|
|
192
|
+
* can start a navigation to a genuine field URL in a frame it scripts and stop it before it
|
|
193
|
+
* commits, and that frame then still runs the page's own document. A frame that navigates
|
|
194
|
+
* elsewhere or detaches is forgotten. Bounded to the 64 most recent frames.
|
|
195
|
+
*/
|
|
196
|
+
export class MicroformFrames {
|
|
197
|
+
frames = new Map();
|
|
198
|
+
contexts = new CaptureContexts();
|
|
199
|
+
/** Start the capture context's check from a navigation that has not committed; records nothing. */
|
|
200
|
+
warm(url) {
|
|
201
|
+
this.contexts.warm(url);
|
|
202
|
+
}
|
|
203
|
+
/** A frame's committed document URL, fragment included. A URL that is not a field iframe's forgets the frame. */
|
|
204
|
+
observe(frameId, url) {
|
|
205
|
+
if (typeof frameId !== 'string' || !frameId || frameId.length > 256)
|
|
206
|
+
return;
|
|
207
|
+
const frame = frameOf(url);
|
|
208
|
+
if (!frame) {
|
|
209
|
+
if (typeof url === 'string' && url)
|
|
210
|
+
this.frames.delete(frameId);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.frames.delete(frameId);
|
|
214
|
+
if (this.frames.size >= MAX_FRAMES)
|
|
215
|
+
this.frames.delete(this.frames.keys().next().value);
|
|
216
|
+
this.frames.set(frameId, frame);
|
|
217
|
+
// Warm the verification so the card request's pause stays synchronous.
|
|
218
|
+
if (frame.microformType === 'card' && frame.reviewed)
|
|
219
|
+
void this.contexts.verify(frame.captureContext);
|
|
220
|
+
}
|
|
221
|
+
forget(frameId) {
|
|
222
|
+
if (typeof frameId === 'string')
|
|
223
|
+
this.frames.delete(frameId);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Judge a POST the frame `frameId` sent. When that frame is not on record, `reread` reads the
|
|
227
|
+
* pausing session's frame tree once (recording what it finds) and resolves to the frame's
|
|
228
|
+
* committed URL, or undefined when the frame was not found or the read failed: a frame read and
|
|
229
|
+
* found to be no field is judged as such, one never read is refused for this request alone.
|
|
230
|
+
*/
|
|
231
|
+
judge(request, frameId, reread) {
|
|
232
|
+
const issuing = frameId ? this.frames.get(frameId) : undefined;
|
|
233
|
+
if (!issuing && frameId && reread) {
|
|
234
|
+
return reread().catch(() => undefined).then((url) => judgeMicroformRequest(request, this.frames.get(frameId) ?? (url === undefined ? undefined : frameOf(url)), this.frames.values(), this.contexts));
|
|
235
|
+
}
|
|
236
|
+
return judgeMicroformRequest(request, issuing, this.frames.values(), this.contexts);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The Playwright adapter's judge: Playwright keeps a frame's fragment in frame.url() (checked in
|
|
241
|
+
* cybersource-flex-browser.test.mjs) and moves frame.url() only when a navigation commits, so the
|
|
242
|
+
* issuing frame and its siblings are read at pause time. `issuingUrl` is undefined when the
|
|
243
|
+
* request's frame could not be read at all.
|
|
244
|
+
*/
|
|
245
|
+
export function judgeMicroformFrameUrls(request, issuingUrl, frameUrls, contexts) {
|
|
246
|
+
const frames = [];
|
|
247
|
+
for (const url of frameUrls) {
|
|
248
|
+
const frame = frameOf(url);
|
|
249
|
+
if (frame)
|
|
250
|
+
frames.push(frame);
|
|
251
|
+
}
|
|
252
|
+
return judgeMicroformRequest(request, issuingUrl === undefined ? undefined : frameOf(issuingUrl), frames, contexts);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Whether a create's sidecar has the shape the API admits (routes/api/v2/checkout.ts
|
|
256
|
+
* createSchema): a capture context of 1 to 16384 characters, and a Microform version with a
|
|
257
|
+
* number field, an optional security code field and a trigger among them. authorize() checks
|
|
258
|
+
* it before any create, so a hand-built request fails locally rather than at the API.
|
|
259
|
+
*/
|
|
260
|
+
export function validCybersourceFlexSidecar(captureContext, microform) {
|
|
261
|
+
if (typeof captureContext !== 'string' || captureContext.length < 1 || captureContext.length > 16384)
|
|
262
|
+
return false;
|
|
263
|
+
if (!microform || typeof microform !== 'object' || Array.isArray(microform))
|
|
264
|
+
return false;
|
|
265
|
+
const value = microform;
|
|
266
|
+
if (Object.keys(value).some((key) => !['version', 'fields', 'trigger'].includes(key)))
|
|
267
|
+
return false;
|
|
268
|
+
if (typeof value.version !== 'string' || !/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(value.version))
|
|
269
|
+
return false;
|
|
270
|
+
const fields = value.fields;
|
|
271
|
+
if (!Array.isArray(fields) || fields.length < 1 || fields.length > 2 || new Set(fields).size !== fields.length
|
|
272
|
+
|| fields.some((field) => !MICROFORM_FIELDS.includes(field)) || !fields.includes('number'))
|
|
273
|
+
return false;
|
|
274
|
+
return value.trigger === undefined || fields.includes(value.trigger);
|
|
275
|
+
}
|
package/dist/gmo.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { GMO_PSP, isGmoTokenHost } from './gmo.generated.js';
|
|
2
|
+
export { GMO_PSP, isGmoTokenHost };
|
|
3
|
+
/** The dark-launch capability a build declares when it pauses only what isGmoSharedPkRequest accepts. */
|
|
4
|
+
export declare const GMO_CAPABILITY = "gmo_shared_pk";
|
|
5
|
+
/**
|
|
6
|
+
* Why a GMO request was aborted as an unsupported checkout, reported as the `reason` of the
|
|
7
|
+
* `unsupported_checkout` event and of the attachment's `unsupported` state:
|
|
8
|
+
* unsupported_gmo_request a request to GMO's CreateToken.json that is not mp-token.js's SHARED_PK
|
|
9
|
+
* getToken for one token
|
|
10
|
+
* unsupported_gmo_token_v1 GMO token v1's JSONP GET to /ext/api/getToken
|
|
11
|
+
*/
|
|
12
|
+
export type GmoUnsupportedReason = 'unsupported_gmo_request' | 'unsupported_gmo_token_v1';
|
|
13
|
+
/**
|
|
14
|
+
* Fetch interception patterns for GMO token v1's JSONP GET, armed only for an org the API served GMO
|
|
15
|
+
* to. A CDP pattern matches the URL as written, path case included, while isGmoTokenV1Request
|
|
16
|
+
* accepts any path case, a trailing dot on the host and any port. So each v1 host is armed whole,
|
|
17
|
+
* in those four spellings, and the pre-claim gate classifies every paused request with
|
|
18
|
+
* isGmoTokenV1Request: the v1 GET is aborted, and anything else on the host continues untouched.
|
|
19
|
+
* Chrome writes the host in lower case, and it blocks a subresource URL that carries credentials.
|
|
20
|
+
*/
|
|
21
|
+
export declare const GMO_TOKEN_V1_PATTERNS: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Whether the vault's current registry recognizes GMO's card request: true only after
|
|
24
|
+
* syncRegistry() for an org the API serves the dark entry to.
|
|
25
|
+
*/
|
|
26
|
+
export declare function recognizesGmo(vault: {
|
|
27
|
+
isCardRequest?: (url: string, method?: string) => boolean;
|
|
28
|
+
} | null | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* GMO token v1's card request: a GET (or, with no method, a URL a route matcher is asked about)
|
|
31
|
+
* to /ext/api/getToken on either v1 host, in any case or with a trailing dot, since the servers
|
|
32
|
+
* behind it need not care.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isGmoTokenV1Request(rawUrl: string, method?: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Whether a POST the registry recognized on a GMO token host is anything but mp-token.js's
|
|
37
|
+
* SHARED_PK getToken for one token: null when the guard accepts it (it pauses for approval), else
|
|
38
|
+
* the reason it is aborted. A guard that throws refuses.
|
|
39
|
+
*/
|
|
40
|
+
export declare function gmoRequestRefusal(request: {
|
|
41
|
+
url: string;
|
|
42
|
+
method: string;
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
body: string | null;
|
|
45
|
+
}): GmoUnsupportedReason | null;
|
|
46
|
+
/**
|
|
47
|
+
* The shop a GMO token request names (encryptionParameters.apiKey), as part of that request's
|
|
48
|
+
* identity. GMO mints a token for that one shop, so a retry that names another shop is a new
|
|
49
|
+
* question and never takes the first request's approval. Undefined when the URL is not a GMO token
|
|
50
|
+
* host; null when the body names no shop that can be read.
|
|
51
|
+
*/
|
|
52
|
+
export declare function gmoRequestShop(url: string, body: string): string | null | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* The only answer the page may receive for an approved GMO request: status 200, and GMO's body
|
|
55
|
+
* reduced to the five fields mp-token.js reads with exactly one 64-hex token
|
|
56
|
+
* (sanitizeGmoTokenResponse), under a JSON content type. Null for anything else, including
|
|
57
|
+
* several tokens, which one approval never pays for.
|
|
58
|
+
*/
|
|
59
|
+
export declare function gmoTokenAnswer(status: number, body: string): {
|
|
60
|
+
status: 200;
|
|
61
|
+
headers: Record<string, string>;
|
|
62
|
+
body: string;
|
|
63
|
+
} | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
declare var GMO_PSP: string;
|
|
2
|
+
declare var GMO_CLIENT_TYPE: string;
|
|
3
|
+
declare var GMO_CONTENT_TYPE: string;
|
|
4
|
+
declare var GMO_TOKEN_PATH: string;
|
|
5
|
+
declare var GMO_TOKEN_HOSTS: Readonly<{
|
|
6
|
+
production: "token.mul-pay.jp";
|
|
7
|
+
sandbox: "stg.token.mul-pay.jp";
|
|
8
|
+
}>;
|
|
9
|
+
declare var GMO_TOKEN_URLS: Readonly<{
|
|
10
|
+
production: `https://token.mul-pay.jp${string}`;
|
|
11
|
+
sandbox: `https://stg.token.mul-pay.jp${string}`;
|
|
12
|
+
}>;
|
|
13
|
+
declare var GMO_SHARED_KEYS: Readonly<{
|
|
14
|
+
"token.mul-pay.jp": Readonly<{
|
|
15
|
+
spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzJ6Ps4kEHAoUxWoTQM2YzvCnJrCpYo3kUKV6G8Pbl7NU1eAkF+rGKhGQVOqULbOa7bS+67txfK+p+WhYIVpxoCzkL9rFKKSb1qeW+phosmN3sXYo0IUa658adLvvpPzpwAqqTu+WNiANZrlstuCC2/dRBwGVl9i8W1pxJZbz2j2lPD7G/jxnqWgfDw+d/EXUAKazS/G6Dk+WoZkmTqkIzsJij5QX4xqmXbdyfsj6BXfmBS1RLAsngJ8bxtshRY9uQ6faSCuTaEF5l38Qh9HhXBoFB+MsOKQ4biUwgW/WaFc5KZ75VDFadrJtUn+5XOlY+iXIQhgdp/zewoZda0rGnQIDAQAB";
|
|
16
|
+
sha256: "c12555cbac901b38910f7f5034a97e353767e1ab6d7c8362c13d0b82f618e142";
|
|
17
|
+
}>;
|
|
18
|
+
"stg.token.mul-pay.jp": Readonly<{
|
|
19
|
+
spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoRuXDRz86jW+7es/BxzFJAmSxsjt69NvjrRCBsmBZb29luUsayl0Ge0pFDXYasVcv0V53S2AZXsikg+dxl3Ykxv0578LMNrKoMvuSDAOZP44/wIaEhN7375754kdubdvK+fHDdW48Q4t2GLrkzMcWjoYgflSW4ldIA/jvnIAine2fK+3ho2WRbIOLHSSwKmAOPI6CKs5v8yN9fg9MvX8IOi2rmFxu0NnYYv32w5qW5BHVzqD1w1ZKfrQHhhFk1Ngkle66NUoyCpVYCsXCJyLwfeN6M2vOIM/ob/K89XXUuAJzSSOgLBVUlSwOoxH35nr1TaKIu5VXp9irYBfiLmJewIDAQAB";
|
|
20
|
+
sha256: "0b065ee8e674fd50f01f9906c1e2393032dfe739eebc9d7b36ba7628312a63d8";
|
|
21
|
+
}>;
|
|
22
|
+
}>;
|
|
23
|
+
declare var GMO_REFUSAL_REASONS: readonly string[];
|
|
24
|
+
declare var GmoError: {
|
|
25
|
+
new (reason: any, message: any): {
|
|
26
|
+
name: string;
|
|
27
|
+
message: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
cause?: unknown;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
declare function gmoTokenEnvironment(rawUrl: any): "production" | "sandbox" | null;
|
|
33
|
+
declare function isGmoTokenHost(rawUrl: any): boolean;
|
|
34
|
+
declare function parseGmoSharedPkBody(body: any, environment: any): {
|
|
35
|
+
createCount: any;
|
|
36
|
+
apiKey: any;
|
|
37
|
+
} | {
|
|
38
|
+
apiKey: any;
|
|
39
|
+
createCount?: undefined;
|
|
40
|
+
} | null;
|
|
41
|
+
declare function isGmoSharedPkRequest(request: any): boolean;
|
|
42
|
+
declare function gmoShopId(request: any): any;
|
|
43
|
+
declare function buildGmoSharedPkBody(templateBody: any, card: any, { crypto, host }?: {}): Promise<string>;
|
|
44
|
+
declare function sanitizeGmoTokenResponse(text: any): string | null;
|
|
45
|
+
declare function gmoTokenMatchesCard(sanitized: any, card: any): boolean;
|
|
46
|
+
declare function gmoRefusalCode(text: any): any;
|
|
47
|
+
declare function gmoResponseFacts(status: any, text: any): {
|
|
48
|
+
outcome: string;
|
|
49
|
+
body: string;
|
|
50
|
+
code?: undefined;
|
|
51
|
+
} | {
|
|
52
|
+
outcome: string;
|
|
53
|
+
body?: undefined;
|
|
54
|
+
code?: undefined;
|
|
55
|
+
} | {
|
|
56
|
+
outcome: string;
|
|
57
|
+
code: any;
|
|
58
|
+
body?: undefined;
|
|
59
|
+
};
|
|
60
|
+
export { GMO_CLIENT_TYPE, GMO_CONTENT_TYPE, GMO_PSP, GMO_REFUSAL_REASONS, GMO_SHARED_KEYS, GMO_TOKEN_HOSTS, GMO_TOKEN_PATH, GMO_TOKEN_URLS, GmoError, buildGmoSharedPkBody, gmoRefusalCode, gmoResponseFacts, gmoShopId, gmoTokenEnvironment, gmoTokenMatchesCard, isGmoSharedPkRequest, isGmoTokenHost, parseGmoSharedPkBody, sanitizeGmoTokenResponse };
|