@sakeetech/vendure-payment-viva 0.2.1

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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +952 -0
  3. package/dist/api/admin-internal.controller.d.ts +59 -0
  4. package/dist/api/admin-internal.controller.d.ts.map +1 -0
  5. package/dist/api/admin-internal.controller.js +229 -0
  6. package/dist/api/admin-internal.controller.js.map +1 -0
  7. package/dist/api/admin-onboarding.controller.d.ts +72 -0
  8. package/dist/api/admin-onboarding.controller.d.ts.map +1 -0
  9. package/dist/api/admin-onboarding.controller.js +496 -0
  10. package/dist/api/admin-onboarding.controller.js.map +1 -0
  11. package/dist/api/admin-sources.controller.d.ts +50 -0
  12. package/dist/api/admin-sources.controller.d.ts.map +1 -0
  13. package/dist/api/admin-sources.controller.js +283 -0
  14. package/dist/api/admin-sources.controller.js.map +1 -0
  15. package/dist/api/shop-api.extension.d.ts +15 -0
  16. package/dist/api/shop-api.extension.d.ts.map +1 -0
  17. package/dist/api/shop-api.extension.js +35 -0
  18. package/dist/api/shop-api.extension.js.map +1 -0
  19. package/dist/api/shop-api.resolver.d.ts +42 -0
  20. package/dist/api/shop-api.resolver.d.ts.map +1 -0
  21. package/dist/api/shop-api.resolver.js +256 -0
  22. package/dist/api/shop-api.resolver.js.map +1 -0
  23. package/dist/api/webhook.controller.d.ts +58 -0
  24. package/dist/api/webhook.controller.d.ts.map +1 -0
  25. package/dist/api/webhook.controller.js +204 -0
  26. package/dist/api/webhook.controller.js.map +1 -0
  27. package/dist/cli/bin.d.ts +28 -0
  28. package/dist/cli/bin.d.ts.map +1 -0
  29. package/dist/cli/bin.js +104 -0
  30. package/dist/cli/bin.js.map +1 -0
  31. package/dist/cli/plan.d.ts +41 -0
  32. package/dist/cli/plan.d.ts.map +1 -0
  33. package/dist/cli/plan.js +115 -0
  34. package/dist/cli/plan.js.map +1 -0
  35. package/dist/cli/register-webhooks.d.ts +45 -0
  36. package/dist/cli/register-webhooks.d.ts.map +1 -0
  37. package/dist/cli/register-webhooks.js +400 -0
  38. package/dist/cli/register-webhooks.js.map +1 -0
  39. package/dist/cli/types.d.ts +75 -0
  40. package/dist/cli/types.d.ts.map +1 -0
  41. package/dist/cli/types.js +10 -0
  42. package/dist/cli/types.js.map +1 -0
  43. package/dist/constants.d.ts +35 -0
  44. package/dist/constants.d.ts.map +1 -0
  45. package/dist/constants.js +40 -0
  46. package/dist/constants.js.map +1 -0
  47. package/dist/entities/index.d.ts +4 -0
  48. package/dist/entities/index.d.ts.map +1 -0
  49. package/dist/entities/index.js +3 -0
  50. package/dist/entities/index.js.map +1 -0
  51. package/dist/entities/viva-transaction.entity.d.ts +70 -0
  52. package/dist/entities/viva-transaction.entity.d.ts.map +1 -0
  53. package/dist/entities/viva-transaction.entity.js +133 -0
  54. package/dist/entities/viva-transaction.entity.js.map +1 -0
  55. package/dist/entities/viva-webhook-event.entity.d.ts +71 -0
  56. package/dist/entities/viva-webhook-event.entity.d.ts.map +1 -0
  57. package/dist/entities/viva-webhook-event.entity.js +138 -0
  58. package/dist/entities/viva-webhook-event.entity.js.map +1 -0
  59. package/dist/index.d.ts +27 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +23 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/jobs/process-viva-webhook.handler.d.ts +95 -0
  64. package/dist/jobs/process-viva-webhook.handler.d.ts.map +1 -0
  65. package/dist/jobs/process-viva-webhook.handler.js +530 -0
  66. package/dist/jobs/process-viva-webhook.handler.js.map +1 -0
  67. package/dist/jobs/queue-names.d.ts +18 -0
  68. package/dist/jobs/queue-names.d.ts.map +1 -0
  69. package/dist/jobs/queue-names.js +19 -0
  70. package/dist/jobs/queue-names.js.map +1 -0
  71. package/dist/jobs/retention-cleanup.handler.d.ts +31 -0
  72. package/dist/jobs/retention-cleanup.handler.d.ts.map +1 -0
  73. package/dist/jobs/retention-cleanup.handler.js +94 -0
  74. package/dist/jobs/retention-cleanup.handler.js.map +1 -0
  75. package/dist/loaders/bootstrap.d.ts +28 -0
  76. package/dist/loaders/bootstrap.d.ts.map +1 -0
  77. package/dist/loaders/bootstrap.js +90 -0
  78. package/dist/loaders/bootstrap.js.map +1 -0
  79. package/dist/migrations/1714000000000-create-viva-tables.d.ts +22 -0
  80. package/dist/migrations/1714000000000-create-viva-tables.d.ts.map +1 -0
  81. package/dist/migrations/1714000000000-create-viva-tables.js +105 -0
  82. package/dist/migrations/1714000000000-create-viva-tables.js.map +1 -0
  83. package/dist/observability/metrics-state.service.d.ts +43 -0
  84. package/dist/observability/metrics-state.service.d.ts.map +1 -0
  85. package/dist/observability/metrics-state.service.js +207 -0
  86. package/dist/observability/metrics-state.service.js.map +1 -0
  87. package/dist/payment-method-handler.d.ts +26 -0
  88. package/dist/payment-method-handler.d.ts.map +1 -0
  89. package/dist/payment-method-handler.js +693 -0
  90. package/dist/payment-method-handler.js.map +1 -0
  91. package/dist/plugin.d.ts +95 -0
  92. package/dist/plugin.d.ts.map +1 -0
  93. package/dist/plugin.js +241 -0
  94. package/dist/plugin.js.map +1 -0
  95. package/dist/providers/viva-oauth2-strategy.provider.d.ts +41 -0
  96. package/dist/providers/viva-oauth2-strategy.provider.d.ts.map +1 -0
  97. package/dist/providers/viva-oauth2-strategy.provider.js +60 -0
  98. package/dist/providers/viva-oauth2-strategy.provider.js.map +1 -0
  99. package/dist/services/connected-accounts.service.d.ts +53 -0
  100. package/dist/services/connected-accounts.service.d.ts.map +1 -0
  101. package/dist/services/connected-accounts.service.js +108 -0
  102. package/dist/services/connected-accounts.service.js.map +1 -0
  103. package/dist/services/per-merchant-semaphore.service.d.ts +49 -0
  104. package/dist/services/per-merchant-semaphore.service.d.ts.map +1 -0
  105. package/dist/services/per-merchant-semaphore.service.js +156 -0
  106. package/dist/services/per-merchant-semaphore.service.js.map +1 -0
  107. package/dist/services/state-machine.service.d.ts +100 -0
  108. package/dist/services/state-machine.service.d.ts.map +1 -0
  109. package/dist/services/state-machine.service.js +233 -0
  110. package/dist/services/state-machine.service.js.map +1 -0
  111. package/dist/types.d.ts +286 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +23 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/util/currency.d.ts +32 -0
  116. package/dist/util/currency.d.ts.map +1 -0
  117. package/dist/util/currency.js +90 -0
  118. package/dist/util/currency.js.map +1 -0
  119. package/dist/util/error-envelope.d.ts +51 -0
  120. package/dist/util/error-envelope.d.ts.map +1 -0
  121. package/dist/util/error-envelope.js +157 -0
  122. package/dist/util/error-envelope.js.map +1 -0
  123. package/dist/util/ip-allowlist.d.ts +44 -0
  124. package/dist/util/ip-allowlist.d.ts.map +1 -0
  125. package/dist/util/ip-allowlist.js +139 -0
  126. package/dist/util/ip-allowlist.js.map +1 -0
  127. package/dist/util/normalize-options.d.ts +24 -0
  128. package/dist/util/normalize-options.d.ts.map +1 -0
  129. package/dist/util/normalize-options.js +189 -0
  130. package/dist/util/normalize-options.js.map +1 -0
  131. package/dist/util/url-template.d.ts +18 -0
  132. package/dist/util/url-template.d.ts.map +1 -0
  133. package/dist/util/url-template.js +22 -0
  134. package/dist/util/url-template.js.map +1 -0
  135. package/package.json +75 -0
@@ -0,0 +1,496 @@
1
+ /**
2
+ * api/admin-onboarding.controller.ts — Connected Accounts onboarding admin REST endpoints.
3
+ *
4
+ * Routes (all under /viva/admin/connected-accounts):
5
+ *
6
+ * POST /viva/admin/connected-accounts
7
+ * Initiate onboarding for a channel. Calls IsvAccounts.createConnectedAccount,
8
+ * writes vivaAccountId to the channel, returns {accountId, onboardingUrl}.
9
+ *
10
+ * GET /viva/admin/connected-accounts/:channelId
11
+ * Return onboarding status: {accountId, merchantId, payoutsEnabled,
12
+ * verificationStatus, applePayDomainVerified}.
13
+ * Optionally hits IsvAccounts.retrieveConnectedAccount (cached 30s) for a
14
+ * richer verificationStatus when the channel has an accountId.
15
+ *
16
+ * POST /viva/admin/connected-accounts/:channelId/reconcile
17
+ * Manual recovery if webhook 8194 was missed. Calls
18
+ * IsvAccounts.retrieveConnectedAccount and, if verified, writes vivaMerchantId
19
+ * then flips vivaPayoutsEnabled=true via ConnectedAccountsService (preserving
20
+ * the mandatory field-write order).
21
+ *
22
+ * Auth: @Allow(Permission.SuperAdmin) on every handler.
23
+ *
24
+ * Error envelope: every error response is VivaPluginError.toJSON() shape.
25
+ *
26
+ * @see docs/plans/vendure-plugin-v0.md §"API Surface — REST endpoints"
27
+ * @see docs/plans/vendure-plugin-v0.md §"Onboarding Flow (§10)"
28
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V9"
29
+ * @see docs/VENDURE-CONTRACT.MD §10
30
+ */
31
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
32
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
33
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
34
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
35
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36
+ };
37
+ var __metadata = (this && this.__metadata) || function (k, v) {
38
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
39
+ };
40
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
41
+ return function (target, key) { decorator(target, key, paramIndex); }
42
+ };
43
+ import { Controller, Post, Get, Param, Body, Res, Inject, UseGuards, } from '@nestjs/common';
44
+ import { Allow, Permission, TransactionalConnection, RequestContextService, Logger, } from '@vendure/core';
45
+ import { AuthGuard } from '@vendure/core';
46
+ import { IsvAccounts, IsvHttpClient } from '@sakeetech/viva-payments-core/isv';
47
+ import { VivaApiError, VivaAuthError, VivaModeMismatchError, } from '@sakeetech/viva-payments-core/errors';
48
+ import { ConnectedAccountsService } from '../services/connected-accounts.service.js';
49
+ import { VivaPluginError } from '../util/error-envelope.js';
50
+ import { VIVA_PLUGIN_OPTIONS, VIVA_OAUTH2_STRATEGY_TOKEN, VIVA_LOG_CONTEXT, } from '../constants.js';
51
+ const STATUS_CACHE_TTL_MS = 30_000;
52
+ const retrieveStatusCache = new Map();
53
+ function getCachedStatus(accountId) {
54
+ const entry = retrieveStatusCache.get(accountId);
55
+ if (!entry)
56
+ return undefined;
57
+ if (Date.now() > entry.expiresAt) {
58
+ retrieveStatusCache.delete(accountId);
59
+ return undefined;
60
+ }
61
+ return entry.verificationStatus;
62
+ }
63
+ function setCachedStatus(accountId, status) {
64
+ retrieveStatusCache.set(accountId, {
65
+ verificationStatus: status,
66
+ expiresAt: Date.now() + STATUS_CACHE_TTL_MS,
67
+ });
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Helpers
71
+ // ---------------------------------------------------------------------------
72
+ /**
73
+ * Derive the local verificationStatus from channel custom fields.
74
+ *
75
+ * Priority: channel custom fields are the authoritative local state.
76
+ * IsvAccounts.retrieve may supply a richer status, but the local state
77
+ * determines the effective gate (payoutsEnabled). When both disagree,
78
+ * channel custom fields win — they were written by the webhook handler
79
+ * which already called retrieve and validated the response.
80
+ */
81
+ function deriveVerificationStatus(cf) {
82
+ if (!cf['vivaAccountId'])
83
+ return 'not_started';
84
+ if (cf['vivaPayoutsEnabled'] === true)
85
+ return 'verified';
86
+ return 'awaiting_kyc';
87
+ }
88
+ /** Extract typed custom fields from a Channel, defensively. */
89
+ function getCustomFields(channel) {
90
+ return channel.customFields ?? {};
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Controller
94
+ // ---------------------------------------------------------------------------
95
+ let AdminOnboardingController = class AdminOnboardingController {
96
+ options;
97
+ oauth2;
98
+ connection;
99
+ requestContextService;
100
+ connectedAccountsService;
101
+ constructor(options, oauth2, connection, requestContextService, connectedAccountsService) {
102
+ this.options = options;
103
+ this.oauth2 = oauth2;
104
+ this.connection = connection;
105
+ this.requestContextService = requestContextService;
106
+ this.connectedAccountsService = connectedAccountsService;
107
+ }
108
+ // -------------------------------------------------------------------------
109
+ // Private helpers
110
+ // -------------------------------------------------------------------------
111
+ /**
112
+ * Narrow options to ISV mode or throw `VIVA_MODE_MISMATCH`.
113
+ * Onboarding is inherently an ISV-only flow (POST /isv/v1/accounts) — the
114
+ * merchant-mode equivalent is direct Self Care signup with no API surface.
115
+ * Slice C will gate the controller registration itself; slice A throws at
116
+ * runtime entry points.
117
+ */
118
+ isvOptions() {
119
+ if (this.options.mode !== 'isv') {
120
+ throw new VivaModeMismatchError({
121
+ message: 'Connected-Accounts onboarding is ISV-only — POST /isv/v1/accounts ' +
122
+ 'is not available under merchant mode.',
123
+ });
124
+ }
125
+ return this.options;
126
+ }
127
+ buildIsvAccounts() {
128
+ const client = new IsvHttpClient({
129
+ environment: this.options.environment,
130
+ authStrategy: this.oauth2,
131
+ });
132
+ return new IsvAccounts(client);
133
+ }
134
+ /**
135
+ * Load a channel by id from the raw TypeORM connection.
136
+ * Returns null if not found.
137
+ */
138
+ async loadChannel(channelId) {
139
+ const repo = this.connection.rawConnection.getRepository('Channel');
140
+ const ch = await repo.findOne({ where: { id: channelId } }).catch(() => null);
141
+ return ch ?? null;
142
+ }
143
+ /**
144
+ * Map a core SDK error to an HTTP status + VivaPluginError JSON body and
145
+ * write it to the response.
146
+ */
147
+ sendVivaError(res, err) {
148
+ if (err instanceof VivaAuthError || (err instanceof VivaApiError && err.httpStatus >= 500)) {
149
+ const pluginErr = VivaPluginError.authDown(err instanceof Error ? err.message : 'Viva service unavailable', err);
150
+ res.writeHead(503, { 'Content-Type': 'application/json' });
151
+ res.end(JSON.stringify(pluginErr.toJSON()));
152
+ return;
153
+ }
154
+ if (err instanceof VivaApiError) {
155
+ const apiErr = err;
156
+ const rawVivaCode = apiErr.vivaCode !== undefined ? Number(apiErr.vivaCode) : undefined;
157
+ // TODO(impl): VivaApiError.vivaCode is string; confirm wire numeric type from live API.
158
+ const pluginErrOpts = {
159
+ message: apiErr.message,
160
+ vivaErrorMessage: apiErr.message,
161
+ cause: err,
162
+ };
163
+ if (rawVivaCode !== undefined && !isNaN(rawVivaCode)) {
164
+ pluginErrOpts.vivaErrorCode = rawVivaCode;
165
+ }
166
+ const pluginErr = VivaPluginError.apiError(pluginErrOpts);
167
+ const httpStatus = apiErr.httpStatus ?? 422;
168
+ res.writeHead(httpStatus >= 400 && httpStatus < 600 ? httpStatus : 422, {
169
+ 'Content-Type': 'application/json',
170
+ });
171
+ res.end(JSON.stringify(pluginErr.toJSON()));
172
+ return;
173
+ }
174
+ // Unexpected internal error
175
+ const pluginErr = VivaPluginError.internalError(err instanceof Error ? err.message : 'Unexpected error', err);
176
+ res.writeHead(500, { 'Content-Type': 'application/json' });
177
+ res.end(JSON.stringify(pluginErr.toJSON()));
178
+ }
179
+ // -------------------------------------------------------------------------
180
+ // POST /viva/admin/connected-accounts — Initiate onboarding
181
+ // -------------------------------------------------------------------------
182
+ async initiateOnboarding(body, res) {
183
+ const { channelId, overrides } = body ?? {};
184
+ if (!channelId) {
185
+ const err = VivaPluginError.channelMisconfigured('channelId is required');
186
+ res.writeHead(400, { 'Content-Type': 'application/json' });
187
+ res.end(JSON.stringify(err.toJSON()));
188
+ return;
189
+ }
190
+ // 1. Load Channel
191
+ const channel = await this.loadChannel(String(channelId));
192
+ if (!channel) {
193
+ const err = VivaPluginError.channelMisconfigured(`Channel ${channelId} not found`);
194
+ res.writeHead(404, { 'Content-Type': 'application/json' });
195
+ res.end(JSON.stringify(err.toJSON()));
196
+ return;
197
+ }
198
+ const cf = getCustomFields(channel);
199
+ // 2. Check Seller exists
200
+ // Vendure Channel has a `seller` relation; surface it from raw TypeORM.
201
+ const channelRepo = this.connection.rawConnection.getRepository('Channel');
202
+ const channelWithSeller = await channelRepo
203
+ .findOne({ where: { id: channel.id }, relations: ['seller'] })
204
+ .catch(() => null);
205
+ const seller = channelWithSeller?.seller;
206
+ if (!seller) {
207
+ const err = VivaPluginError.channelMisconfigured('Channel must have a Seller before onboarding');
208
+ res.writeHead(400, { 'Content-Type': 'application/json' });
209
+ res.end(JSON.stringify(err.toJSON()));
210
+ return;
211
+ }
212
+ // 3. Already onboarded?
213
+ const existingAccountId = cf['vivaAccountId'];
214
+ if (existingAccountId) {
215
+ const err = VivaPluginError.alreadyOnboarded(existingAccountId);
216
+ res.writeHead(409, { 'Content-Type': 'application/json' });
217
+ res.end(JSON.stringify(err.toJSON()));
218
+ return;
219
+ }
220
+ // 4. Build payload from Seller fields + plugin config + overrides
221
+ // POST /isv/v1/accounts accepts: email (required), returnUrl (required),
222
+ // branding (optional: partnerName + logoUrl required, primaryColor optional).
223
+ // The ISV API collects countryCode, firstName, lastName, currency, etc. from
224
+ // the merchant during Viva-hosted KYC; the plugin does not forward them.
225
+ const sellerCf = seller['customFields'] ?? {};
226
+ const email = overrides?.email ??
227
+ sellerCf['contactEmail'] ??
228
+ sellerCf['email'] ??
229
+ seller['email'];
230
+ const ctxForResolvers = {
231
+ channelId: channel.id,
232
+ channel: {
233
+ id: channel.id,
234
+ code: channel.code,
235
+ customFields: cf,
236
+ },
237
+ apiType: 'admin',
238
+ };
239
+ const isvOpts = this.isvOptions();
240
+ const returnUrl = overrides?.returnUrl ??
241
+ (typeof isvOpts.onboardingReturnUrl === 'function'
242
+ ? isvOpts.onboardingReturnUrl(ctxForResolvers)
243
+ : isvOpts.onboardingReturnUrl);
244
+ if (!email) {
245
+ const err = VivaPluginError.channelMisconfigured('Seller must have an email (customFields.contactEmail, customFields.email, or seller.email) — or pass `overrides.email`.');
246
+ res.writeHead(400, { 'Content-Type': 'application/json' });
247
+ res.end(JSON.stringify(err.toJSON()));
248
+ return;
249
+ }
250
+ if (!returnUrl) {
251
+ const err = VivaPluginError.channelMisconfigured('`onboardingReturnUrl` is required in plugin options (or pass `overrides.returnUrl`).');
252
+ res.writeHead(400, { 'Content-Type': 'application/json' });
253
+ res.end(JSON.stringify(err.toJSON()));
254
+ return;
255
+ }
256
+ const basePayload = {
257
+ email,
258
+ returnUrl,
259
+ ...(overrides?.branding
260
+ ? { branding: overrides.branding }
261
+ : isvOpts.onboardingBranding
262
+ ? { branding: isvOpts.onboardingBranding }
263
+ : {}),
264
+ };
265
+ Logger.info(`[AdminOnboarding] Initiating onboarding for channel ${String(channel.id)}.`, VIVA_LOG_CONTEXT);
266
+ // 5. Call IsvAccounts.createConnectedAccount
267
+ let accountId;
268
+ let onboardingUrl;
269
+ try {
270
+ const isvAccounts = this.buildIsvAccounts();
271
+ const result = await isvAccounts.createConnectedAccount(basePayload);
272
+ accountId = result.accountId;
273
+ onboardingUrl = result.invitation.redirectUrl;
274
+ }
275
+ catch (err) {
276
+ Logger.error(`[AdminOnboarding] IsvAccounts.createConnectedAccount failed for channel ${String(channel.id)}: ${String(err)}`, VIVA_LOG_CONTEXT);
277
+ this.sendVivaError(res, err);
278
+ return;
279
+ }
280
+ // 6. Write accountId to channel via ConnectedAccountsService
281
+ try {
282
+ const ctx = await this.requestContextService.create({
283
+ apiType: 'admin',
284
+ channelOrToken: channel,
285
+ });
286
+ await this.connectedAccountsService.writeAccountId(ctx, channel, accountId);
287
+ }
288
+ catch (err) {
289
+ Logger.error(`[AdminOnboarding] Failed to write vivaAccountId to channel ${String(channel.id)}: ${String(err)}`, VIVA_LOG_CONTEXT);
290
+ // accountId was created at Viva — still return it so the admin can record it
291
+ Logger.warn(`[AdminOnboarding] accountId=${accountId} was created at Viva but could not be persisted locally. Manual recovery required.`, VIVA_LOG_CONTEXT);
292
+ }
293
+ // 7. Return response
294
+ res.writeHead(200, { 'Content-Type': 'application/json' });
295
+ res.end(JSON.stringify({
296
+ accountId,
297
+ onboardingUrl,
298
+ channelId: channel.id,
299
+ instructions: 'Send onboardingUrl to the shop principal to complete KYC.',
300
+ }));
301
+ }
302
+ // -------------------------------------------------------------------------
303
+ // GET /viva/admin/connected-accounts/:channelId — Onboarding status
304
+ // -------------------------------------------------------------------------
305
+ async getStatus(channelId, res) {
306
+ const channel = await this.loadChannel(channelId);
307
+ if (!channel) {
308
+ const err = VivaPluginError.channelMisconfigured(`Channel ${channelId} not found`);
309
+ res.writeHead(404, { 'Content-Type': 'application/json' });
310
+ res.end(JSON.stringify(err.toJSON()));
311
+ return;
312
+ }
313
+ const cf = getCustomFields(channel);
314
+ const accountId = cf['vivaAccountId'] ?? null;
315
+ const merchantId = cf['vivaMerchantId'] ?? null;
316
+ const payoutsEnabled = cf['vivaPayoutsEnabled'] ?? false;
317
+ const applePayDomainVerified = cf['vivaApplePayDomainVerified'] ?? false;
318
+ // Derive local status from channel fields
319
+ const localStatus = deriveVerificationStatus(cf);
320
+ // Optionally enrich with Viva's status (cached 30s) when accountId is known
321
+ let verificationStatus = localStatus;
322
+ if (accountId) {
323
+ const cached = getCachedStatus(accountId);
324
+ if (cached !== undefined) {
325
+ // Use cached Viva status only for informational display;
326
+ // local field state (payoutsEnabled) governs the actual gate.
327
+ // If Viva says 'verified' but local hasn't flipped yet, local wins.
328
+ // This avoids race conditions during the webhook-write window.
329
+ // Strategy: show the more favorable (local if verified, viva if richer).
330
+ if (payoutsEnabled) {
331
+ verificationStatus = 'verified';
332
+ }
333
+ else if (cached === 'verified') {
334
+ // Viva says verified but local webhook hasn't landed yet — show awaiting_kyc
335
+ // to avoid premature gate flip via a status endpoint poll.
336
+ // TODO(impl): expose vivaRawStatus as a separate field if SaaS needs it.
337
+ verificationStatus = 'awaiting_kyc';
338
+ }
339
+ }
340
+ else {
341
+ // Attempt a fresh retrieve — best effort, don't fail the request
342
+ try {
343
+ const isvAccounts = this.buildIsvAccounts();
344
+ const retrieved = await isvAccounts.retrieveConnectedAccount(accountId);
345
+ const rawStatus = retrieved.verified ? 'verified' : 'pending';
346
+ setCachedStatus(accountId, rawStatus);
347
+ // Same local-wins rule as above
348
+ if (payoutsEnabled) {
349
+ verificationStatus = 'verified';
350
+ }
351
+ // rawStatus from Viva does not override local payoutsEnabled flag
352
+ }
353
+ catch (err) {
354
+ // Best effort — log and proceed with local state
355
+ Logger.warn(`[AdminOnboarding] IsvAccounts.retrieveConnectedAccount failed for accountId=${accountId}: ${String(err)}`, VIVA_LOG_CONTEXT);
356
+ }
357
+ }
358
+ }
359
+ const payload = {
360
+ channelId: channel.id,
361
+ accountId,
362
+ merchantId,
363
+ payoutsEnabled,
364
+ verificationStatus,
365
+ applePayDomainVerified,
366
+ };
367
+ res.writeHead(200, { 'Content-Type': 'application/json' });
368
+ res.end(JSON.stringify(payload));
369
+ }
370
+ // -------------------------------------------------------------------------
371
+ // POST /viva/admin/connected-accounts/:channelId/reconcile — Manual recovery
372
+ // -------------------------------------------------------------------------
373
+ async reconcile(channelId, res) {
374
+ const channel = await this.loadChannel(channelId);
375
+ if (!channel) {
376
+ const err = VivaPluginError.channelMisconfigured(`Channel ${channelId} not found`);
377
+ res.writeHead(404, { 'Content-Type': 'application/json' });
378
+ res.end(JSON.stringify(err.toJSON()));
379
+ return;
380
+ }
381
+ const cf = getCustomFields(channel);
382
+ const accountId = cf['vivaAccountId'];
383
+ if (!accountId) {
384
+ const err = VivaPluginError.channelMisconfigured('Channel does not have a vivaAccountId. Initiate onboarding first.');
385
+ res.writeHead(400, { 'Content-Type': 'application/json' });
386
+ res.end(JSON.stringify(err.toJSON()));
387
+ return;
388
+ }
389
+ // Already verified — idempotent no-op
390
+ if (cf['vivaPayoutsEnabled'] === true && cf['vivaMerchantId']) {
391
+ const payload = {
392
+ channelId: channel.id,
393
+ accountId,
394
+ merchantId: cf['vivaMerchantId'],
395
+ payoutsEnabled: true,
396
+ verificationStatus: 'verified',
397
+ applePayDomainVerified: cf['vivaApplePayDomainVerified'] ?? false,
398
+ };
399
+ res.writeHead(200, { 'Content-Type': 'application/json' });
400
+ res.end(JSON.stringify(payload));
401
+ return;
402
+ }
403
+ // Call IsvAccounts.retrieveConnectedAccount
404
+ let retrieved;
405
+ try {
406
+ const isvAccounts = this.buildIsvAccounts();
407
+ retrieved = await isvAccounts.retrieveConnectedAccount(accountId);
408
+ }
409
+ catch (err) {
410
+ Logger.error(`[AdminOnboarding] reconcile: IsvAccounts.retrieveConnectedAccount failed for channel ${channelId}: ${String(err)}`, VIVA_LOG_CONTEXT);
411
+ this.sendVivaError(res, err);
412
+ return;
413
+ }
414
+ const isVerified = retrieved.verified === true;
415
+ const merchantId = retrieved.merchantId;
416
+ if (isVerified && merchantId) {
417
+ // Write in the mandatory order: vivaMerchantId FIRST, vivaPayoutsEnabled LAST
418
+ try {
419
+ const ctx = await this.requestContextService.create({
420
+ apiType: 'admin',
421
+ channelOrToken: channel,
422
+ });
423
+ await this.connectedAccountsService.writeMerchantId(ctx, channel, merchantId);
424
+ await this.connectedAccountsService.flipPayoutsEnabled(ctx, channel, true);
425
+ Logger.info(`[AdminOnboarding] reconcile: wrote merchantId=${merchantId} and flipped payoutsEnabled for channel ${channelId}.`, VIVA_LOG_CONTEXT);
426
+ }
427
+ catch (err) {
428
+ Logger.error(`[AdminOnboarding] reconcile: failed to write channel fields for channel ${channelId}: ${String(err)}`, VIVA_LOG_CONTEXT);
429
+ const pluginErr = VivaPluginError.internalError('Failed to persist reconciliation data', err);
430
+ res.writeHead(500, { 'Content-Type': 'application/json' });
431
+ res.end(JSON.stringify(pluginErr.toJSON()));
432
+ return;
433
+ }
434
+ const payload = {
435
+ channelId: channel.id,
436
+ accountId,
437
+ merchantId,
438
+ payoutsEnabled: true,
439
+ verificationStatus: 'verified',
440
+ applePayDomainVerified: cf['vivaApplePayDomainVerified'] ?? false,
441
+ };
442
+ res.writeHead(200, { 'Content-Type': 'application/json' });
443
+ res.end(JSON.stringify(payload));
444
+ return;
445
+ }
446
+ // Viva says still pending — no writes; return current state
447
+ const payload = {
448
+ channelId: channel.id,
449
+ accountId,
450
+ merchantId: cf['vivaMerchantId'] ?? null,
451
+ payoutsEnabled: false,
452
+ verificationStatus: 'awaiting_kyc',
453
+ applePayDomainVerified: cf['vivaApplePayDomainVerified'] ?? false,
454
+ };
455
+ res.writeHead(200, { 'Content-Type': 'application/json' });
456
+ res.end(JSON.stringify(payload));
457
+ }
458
+ };
459
+ __decorate([
460
+ Post(),
461
+ Allow(Permission.SuperAdmin),
462
+ __param(0, Body()),
463
+ __param(1, Res()),
464
+ __metadata("design:type", Function),
465
+ __metadata("design:paramtypes", [Object, Function]),
466
+ __metadata("design:returntype", Promise)
467
+ ], AdminOnboardingController.prototype, "initiateOnboarding", null);
468
+ __decorate([
469
+ Get(':channelId'),
470
+ Allow(Permission.SuperAdmin),
471
+ __param(0, Param('channelId')),
472
+ __param(1, Res()),
473
+ __metadata("design:type", Function),
474
+ __metadata("design:paramtypes", [String, Function]),
475
+ __metadata("design:returntype", Promise)
476
+ ], AdminOnboardingController.prototype, "getStatus", null);
477
+ __decorate([
478
+ Post(':channelId/reconcile'),
479
+ Allow(Permission.SuperAdmin),
480
+ __param(0, Param('channelId')),
481
+ __param(1, Res()),
482
+ __metadata("design:type", Function),
483
+ __metadata("design:paramtypes", [String, Function]),
484
+ __metadata("design:returntype", Promise)
485
+ ], AdminOnboardingController.prototype, "reconcile", null);
486
+ AdminOnboardingController = __decorate([
487
+ Controller('viva/admin/connected-accounts'),
488
+ UseGuards(AuthGuard),
489
+ __param(0, Inject(VIVA_PLUGIN_OPTIONS)),
490
+ __param(1, Inject(VIVA_OAUTH2_STRATEGY_TOKEN)),
491
+ __metadata("design:paramtypes", [Object, Object, TransactionalConnection,
492
+ RequestContextService,
493
+ ConnectedAccountsService])
494
+ ], AdminOnboardingController);
495
+ export { AdminOnboardingController };
496
+ //# sourceMappingURL=admin-onboarding.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-onboarding.controller.js","sourceRoot":"","sources":["../../src/api/admin-onboarding.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * api/admin-sources.controller.ts — ISV-only admin REST for /api/sources wrapping.
3
+ *
4
+ * Single route:
5
+ *
6
+ * POST /viva/admin/connected-accounts/:id/sources
7
+ * Creates a payment source (ecommerce or physical) on a connected
8
+ * merchant's Viva account via POST /api/sources on the legacy host,
9
+ * authenticated with Reseller Basic auth (per AUTH.md §1.2).
10
+ *
11
+ * Flow:
12
+ * 1. Mode gate: 404 in merchant mode (mirrors Medusa _mode-gate.ts).
13
+ * 2. Reseller gate: 412 VIVA_RESELLER_CREDENTIALS_MISSING when
14
+ * options.reseller is absent.
15
+ * 3. Body parse: 400 on missing/invalid fields or unknown kind.
16
+ * 4. Account lookup: IsvAccounts.retrieveConnectedAccount(:id).
17
+ * → 409 VIVA_ACCOUNT_NOT_VERIFIED when verified !== true or
18
+ * merchantId is null.
19
+ * 5. BasicAuthClient {authVariant:'reseller'} built with the connected
20
+ * merchant's UUID (account.merchantId — NOT config.reseller.merchantId).
21
+ * 6. IsvSources.createEcommerceSource / createPhysicalSource.
22
+ * 7. 201 with {sourceCode, name, kind}.
23
+ *
24
+ * Viva 4xx on /api/sources → 422 VIVA_SOURCE_CREATION_FAILED.
25
+ * Viva 5xx / auth errors → 503 VIVA_AUTH_DOWN (existing envelope rule).
26
+ *
27
+ * @see docs/plans/multi-mode-v0.md §6 (admin REST table)
28
+ * @see docs/AUTH.md §1.2 (Reseller Basic)
29
+ * @see docs/ENDPOINTS.md §5.1
30
+ * @see packages/medusa-payment-viva/src/api/viva/admin/connected-accounts/[id]/sources/route.ts
31
+ */
32
+ import type { ServerResponse } from 'node:http';
33
+ import type { VivaPaymentPluginOptions } from '../types.js';
34
+ import type { VivaOAuth2Strategy } from '../providers/viva-oauth2-strategy.provider.js';
35
+ export declare class AdminSourcesController {
36
+ private readonly options;
37
+ private readonly oauth2;
38
+ constructor(options: VivaPaymentPluginOptions, oauth2: VivaOAuth2Strategy);
39
+ /**
40
+ * Narrow options to ISV mode + reseller block.
41
+ * Returns:
42
+ * - { ok: true, isv } on success.
43
+ * - { ok: false, status, err } on mode mismatch (404) or missing reseller (412).
44
+ */
45
+ private requireIsvWithReseller;
46
+ /** Build the OAuth2-backed IsvAccounts client (platform creds). */
47
+ private buildIsvAccounts;
48
+ createSource(id: string, body: unknown, res: ServerResponse): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=admin-sources.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-sources.controller.d.ts","sourceRoot":"","sources":["../../src/api/admin-sources.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAWH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAchD,OAAO,KAAK,EACV,wBAAwB,EAEzB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+CAA+C,CAAC;AA+GxF,qBAEa,sBAAsB;IAG/B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,OAAO,EAAE,wBAAwB,EAEjC,MAAM,EAAE,kBAAkB;IAG7C;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IA2B9B,mEAAmE;IACnE,OAAO,CAAC,gBAAgB;IAclB,YAAY,CACH,EAAE,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACd,GAAG,EAAE,cAAc,GACzB,OAAO,CAAC,IAAI,CAAC;CAgJjB"}