@alphawebai/hs-prism 0.1.0-rc.30 → 0.1.0-rc.32
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const ALPHAWEB_STRIPE_WEBHOOK_EVENTS: readonly ["charge.dispute.closed", "charge.dispute.created", "charge.dispute.funds_reinstated", "charge.dispute.funds_withdrawn", "charge.dispute.updated", "mandate.updated", "payment_intent.amount_capturable_updated", "payment_intent.canceled", "payment_intent.payment_failed", "payment_intent.processing", "payment_intent.requires_action", "payment_intent.succeeded", "payment_method.attached", "payment_method.automatically_updated", "payment_method.detached", "payment_method.updated", "refund.created", "refund.failed", "refund.updated", "setup_intent.canceled", "setup_intent.requires_action", "setup_intent.setup_failed", "setup_intent.succeeded"];
|
|
2
2
|
export declare const ALPHAWEB_PAYPAL_WEBHOOK_EVENTS: readonly ["CHECKOUT.ORDER.APPROVED", "CHECKOUT.ORDER.COMPLETED", "CHECKOUT.ORDER.PROCESSED", "CHECKOUT.PAYMENT-APPROVAL.REVERSED", "CUSTOMER.DISPUTE.CREATED", "CUSTOMER.DISPUTE.RESOLVED", "CUSTOMER.DISPUTE.UPDATED", "PAYMENT.AUTHORIZATION.CREATED", "PAYMENT.AUTHORIZATION.VOIDED", "PAYMENT.CAPTURE.COMPLETED", "PAYMENT.CAPTURE.DECLINED", "PAYMENT.CAPTURE.PENDING", "PAYMENT.CAPTURE.REFUNDED", "PAYMENT.CAPTURE.REVERSED", "PAYMENT.REFUND.FAILED", "PAYMENT.REFUND.PENDING", "RISK.DISPUTE.CREATED", "VAULT.PAYMENT-TOKEN.CREATED", "VAULT.PAYMENT-TOKEN.DELETED", "VAULT.PAYMENT-TOKEN.DELETION-INITIATED"];
|
|
3
3
|
export declare const ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST: Readonly<{
|
|
4
|
-
readonly release: "0.1.0-rc.
|
|
4
|
+
readonly release: "0.1.0-rc.32";
|
|
5
5
|
readonly upstreamCommit: "b07af7d9dac271de729548e74217ddcf895c602b";
|
|
6
6
|
readonly medusaRange: ">=2.17.2 <2.18.0";
|
|
7
7
|
readonly stripeApiVersion: "2026-06-24.dahlia";
|
|
@@ -49,7 +49,7 @@ exports.ALPHAWEB_PAYPAL_WEBHOOK_EVENTS = Object.freeze([
|
|
|
49
49
|
'VAULT.PAYMENT-TOKEN.DELETION-INITIATED',
|
|
50
50
|
]);
|
|
51
51
|
exports.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST = Object.freeze({
|
|
52
|
-
release: '0.1.0-rc.
|
|
52
|
+
release: '0.1.0-rc.32',
|
|
53
53
|
upstreamCommit: 'b07af7d9dac271de729548e74217ddcf895c602b',
|
|
54
54
|
medusaRange: '>=2.17.2 <2.18.0',
|
|
55
55
|
stripeApiVersion: '2026-06-24.dahlia',
|
|
@@ -349,6 +349,9 @@ function paypalWebhookObjectKind(type) {
|
|
|
349
349
|
}
|
|
350
350
|
if (type.startsWith('PAYMENT.AUTHORIZATION.'))
|
|
351
351
|
return 'authorization';
|
|
352
|
+
if (type === 'PAYMENT.CAPTURE.REFUNDED' || type === 'PAYMENT.CAPTURE.REVERSED') {
|
|
353
|
+
return 'refund';
|
|
354
|
+
}
|
|
352
355
|
if (type.startsWith('PAYMENT.CAPTURE.'))
|
|
353
356
|
return 'capture';
|
|
354
357
|
if (type.startsWith('PAYMENT.REFUND.'))
|
|
@@ -396,8 +399,49 @@ function paypalWebhookPurchaseUnit(resource) {
|
|
|
396
399
|
return {};
|
|
397
400
|
return recordOf(resource.purchase_units[0]);
|
|
398
401
|
}
|
|
399
|
-
function
|
|
400
|
-
|
|
402
|
+
function paypalWebhookLinkedId(resource, collection) {
|
|
403
|
+
if (!Array.isArray(resource.links))
|
|
404
|
+
return null;
|
|
405
|
+
for (const linkValue of resource.links) {
|
|
406
|
+
const link = recordOf(linkValue);
|
|
407
|
+
const relationship = stringOf(link.rel);
|
|
408
|
+
if (collection === 'orders' && relationship !== 'up')
|
|
409
|
+
continue;
|
|
410
|
+
if (collection === 'captures' && relationship !== 'up' && relationship !== 'capture')
|
|
411
|
+
continue;
|
|
412
|
+
const href = stringOf(link.href);
|
|
413
|
+
if (!href)
|
|
414
|
+
continue;
|
|
415
|
+
try {
|
|
416
|
+
const segments = new URL(href).pathname.split('/').filter(Boolean);
|
|
417
|
+
const collectionIndex = segments.findIndex((segment) => segment === collection);
|
|
418
|
+
const id = collectionIndex >= 0 ? segments[collectionIndex + 1] : undefined;
|
|
419
|
+
if (id)
|
|
420
|
+
return id;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
// An invalid provider link is not authoritative correlation evidence.
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
function paypalDisputeTransactionInfo(resource) {
|
|
429
|
+
if (!Array.isArray(resource.disputed_transactions))
|
|
430
|
+
return [];
|
|
431
|
+
return resource.disputed_transactions.map((transactionValue) => {
|
|
432
|
+
const transaction = recordOf(transactionValue);
|
|
433
|
+
const transactionInfo = recordOf(transaction.transaction_info);
|
|
434
|
+
return Object.keys(transactionInfo).length > 0 ? transactionInfo : transaction;
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
function consistentString(values) {
|
|
438
|
+
if (values.length === 0 || values.some((value) => value === null))
|
|
439
|
+
return null;
|
|
440
|
+
const unique = new Set(values);
|
|
441
|
+
return unique.size === 1 ? [...unique][0] : null;
|
|
442
|
+
}
|
|
443
|
+
function paypalWebhookMoney(resource, purchaseUnit, objectKind) {
|
|
444
|
+
const amount = recordOf(objectKind === 'dispute' ? resource.dispute_amount : resource.amount ?? purchaseUnit.amount);
|
|
401
445
|
const currency = stringOf(amount.currency_code)?.toLowerCase();
|
|
402
446
|
const value = stringOf(amount.value);
|
|
403
447
|
if (!currency || !value)
|
|
@@ -412,6 +456,58 @@ function paypalWebhookMoney(resource, purchaseUnit) {
|
|
|
412
456
|
return null;
|
|
413
457
|
}
|
|
414
458
|
}
|
|
459
|
+
function paypalWebhookRefundStatus(type, resource) {
|
|
460
|
+
if (type === 'PAYMENT.CAPTURE.REFUNDED' || type === 'PAYMENT.CAPTURE.REVERSED') {
|
|
461
|
+
return 'succeeded';
|
|
462
|
+
}
|
|
463
|
+
if (type === 'PAYMENT.REFUND.FAILED')
|
|
464
|
+
return 'failed';
|
|
465
|
+
if (type === 'PAYMENT.REFUND.PENDING')
|
|
466
|
+
return 'pending';
|
|
467
|
+
const status = stringOf(resource.status)?.toUpperCase();
|
|
468
|
+
if (status === 'COMPLETED' || status === 'REFUNDED' || status === 'REVERSED') {
|
|
469
|
+
return 'succeeded';
|
|
470
|
+
}
|
|
471
|
+
if (status === 'PENDING')
|
|
472
|
+
return 'pending';
|
|
473
|
+
if (status === 'FAILED' || status === 'DECLINED' || status === 'DENIED')
|
|
474
|
+
return 'failed';
|
|
475
|
+
if (status === 'CANCELED' || status === 'CANCELLED')
|
|
476
|
+
return 'canceled';
|
|
477
|
+
return 'unknown';
|
|
478
|
+
}
|
|
479
|
+
function paypalWebhookSafeData(type, resource, disputeTransactions, parentCaptureId) {
|
|
480
|
+
const safeData = Object.fromEntries(['status', 'reason'].flatMap((key) => {
|
|
481
|
+
const value = resource[key];
|
|
482
|
+
return value === null || ['string', 'number', 'boolean'].includes(typeof value)
|
|
483
|
+
? [[key, value]]
|
|
484
|
+
: [];
|
|
485
|
+
}));
|
|
486
|
+
if (parentCaptureId)
|
|
487
|
+
safeData.parent_capture_id = parentCaptureId;
|
|
488
|
+
if (type === 'PAYMENT.CAPTURE.REFUNDED')
|
|
489
|
+
safeData.refund_semantics = 'merchant_refund';
|
|
490
|
+
if (type === 'PAYMENT.CAPTURE.REVERSED')
|
|
491
|
+
safeData.refund_semantics = 'provider_reversal';
|
|
492
|
+
if (type.includes('DISPUTE')) {
|
|
493
|
+
const rawStatus = stringOf(resource.status)?.toUpperCase();
|
|
494
|
+
safeData.dispute_status =
|
|
495
|
+
type === 'CUSTOMER.DISPUTE.RESOLVED' || rawStatus === 'RESOLVED' ? 'closed' : 'opened';
|
|
496
|
+
const lifeCycleStage = stringOf(resource.dispute_life_cycle_stage);
|
|
497
|
+
if (lifeCycleStage)
|
|
498
|
+
safeData.dispute_life_cycle_stage = lifeCycleStage;
|
|
499
|
+
const outcomeCode = stringOf(recordOf(resource.dispute_outcome).outcome_code);
|
|
500
|
+
if (outcomeCode)
|
|
501
|
+
safeData.outcome_code = outcomeCode;
|
|
502
|
+
const sellerTransactionId = consistentString(disputeTransactions.map((transaction) => stringOf(transaction.seller_transaction_id)));
|
|
503
|
+
if (sellerTransactionId)
|
|
504
|
+
safeData.paypal_seller_transaction_id = sellerTransactionId;
|
|
505
|
+
const buyerTransactionId = consistentString(disputeTransactions.map((transaction) => stringOf(transaction.buyer_transaction_id)));
|
|
506
|
+
if (buyerTransactionId)
|
|
507
|
+
safeData.paypal_buyer_transaction_id = buyerTransactionId;
|
|
508
|
+
}
|
|
509
|
+
return safeData;
|
|
510
|
+
}
|
|
415
511
|
function normalizePayPalWebhook(input, binding) {
|
|
416
512
|
let parsed;
|
|
417
513
|
try {
|
|
@@ -427,33 +523,24 @@ function normalizePayPalWebhook(input, binding) {
|
|
|
427
523
|
const resourceVersion = stringOf(event.resource_version);
|
|
428
524
|
const resource = recordOf(event.resource);
|
|
429
525
|
const purchaseUnit = paypalWebhookPurchaseUnit(resource);
|
|
430
|
-
const
|
|
526
|
+
const objectKind = paypalWebhookObjectKind(type ?? '');
|
|
527
|
+
const objectId = objectKind === 'dispute' ? stringOf(resource.dispute_id) : stringOf(resource.id);
|
|
431
528
|
if (!providerEventId || !type || !createdAt || !objectId) {
|
|
432
529
|
throw validationError('The verified PayPal event identity is incomplete.', 'PRISM_WEBHOOK_IDENTITY');
|
|
433
530
|
}
|
|
434
531
|
const related = recordOf(recordOf(resource.supplementary_data).related_ids);
|
|
435
|
-
const linkedOrderId =
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return orderIndex >= 0 && segments[orderIndex + 1] ? [segments[orderIndex + 1]] : [];
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
return [];
|
|
449
|
-
}
|
|
450
|
-
})[0] ?? null
|
|
451
|
-
: null;
|
|
452
|
-
const invoiceId = stringOf(resource.invoice_id) ?? stringOf(purchaseUnit.invoice_id);
|
|
453
|
-
const medusaPaymentSessionId = stringOf(resource.custom_id) ?? stringOf(purchaseUnit.custom_id);
|
|
532
|
+
const linkedOrderId = paypalWebhookLinkedId(resource, 'orders');
|
|
533
|
+
const linkedCaptureId = paypalWebhookLinkedId(resource, 'captures');
|
|
534
|
+
const parentCaptureId = stringOf(related.capture_id) ?? stringOf(resource.capture_id) ?? linkedCaptureId;
|
|
535
|
+
const disputeTransactions = paypalDisputeTransactionInfo(resource);
|
|
536
|
+
const invoiceId = objectKind === 'dispute'
|
|
537
|
+
? consistentString(disputeTransactions.map((transaction) => stringOf(transaction.invoice_number)))
|
|
538
|
+
: stringOf(resource.invoice_id) ?? stringOf(purchaseUnit.invoice_id);
|
|
539
|
+
const medusaPaymentSessionId = objectKind === 'dispute'
|
|
540
|
+
? consistentString(disputeTransactions.map((transaction) => stringOf(transaction.custom)))
|
|
541
|
+
: stringOf(resource.custom_id) ?? stringOf(purchaseUnit.custom_id);
|
|
454
542
|
const connectorPaymentId = stringOf(related.order_id) ??
|
|
455
543
|
linkedOrderId ??
|
|
456
|
-
stringOf(resource.invoice_id) ??
|
|
457
544
|
(type.startsWith('CHECKOUT.ORDER.') ? objectId : null);
|
|
458
545
|
const bodyDigest = (0, node_crypto_1.createHash)('sha256').update(input.rawBody).digest('hex');
|
|
459
546
|
const envelope = {
|
|
@@ -465,7 +552,7 @@ function normalizePayPalWebhook(input, binding) {
|
|
|
465
552
|
apiVersion: resourceVersion,
|
|
466
553
|
type,
|
|
467
554
|
createdAt,
|
|
468
|
-
objectKind
|
|
555
|
+
objectKind,
|
|
469
556
|
objectId,
|
|
470
557
|
connectorPaymentId,
|
|
471
558
|
paymentIntentId: null,
|
|
@@ -481,12 +568,12 @@ function normalizePayPalWebhook(input, binding) {
|
|
|
481
568
|
},
|
|
482
569
|
};
|
|
483
570
|
}
|
|
484
|
-
const money = paypalWebhookMoney(resource, purchaseUnit);
|
|
571
|
+
const money = paypalWebhookMoney(resource, purchaseUnit, objectKind);
|
|
485
572
|
const paymentSourceVault = recordOf(recordOf(recordOf(recordOf(resource.payment_source).paypal).attributes).vault);
|
|
486
573
|
const vaultDetails = type.startsWith('VAULT.PAYMENT-TOKEN.')
|
|
487
574
|
? resource
|
|
488
575
|
: paymentSourceVault;
|
|
489
|
-
const merchantOperationId = invoiceId
|
|
576
|
+
const merchantOperationId = invoiceId;
|
|
490
577
|
const normalized = {
|
|
491
578
|
...envelope,
|
|
492
579
|
customerId: stringOf(recordOf(vaultDetails.customer).id) ??
|
|
@@ -497,24 +584,39 @@ function normalizePayPalWebhook(input, binding) {
|
|
|
497
584
|
medusaPaymentSessionId,
|
|
498
585
|
status: paypalWebhookStatus(type, resource),
|
|
499
586
|
money,
|
|
500
|
-
refunds:
|
|
587
|
+
refunds: objectKind === 'refund' && money && parentCaptureId
|
|
501
588
|
? [
|
|
502
589
|
{
|
|
503
590
|
refundId: objectId,
|
|
504
|
-
chargeId:
|
|
591
|
+
chargeId: parentCaptureId,
|
|
505
592
|
paymentIntentId: null,
|
|
506
|
-
status: type
|
|
593
|
+
status: paypalWebhookRefundStatus(type, resource),
|
|
507
594
|
money,
|
|
508
595
|
},
|
|
509
596
|
]
|
|
510
597
|
: [],
|
|
511
|
-
safeData:
|
|
512
|
-
const value = resource[key];
|
|
513
|
-
return value === null || ['string', 'number', 'boolean'].includes(typeof value)
|
|
514
|
-
? [[key, value]]
|
|
515
|
-
: [];
|
|
516
|
-
})),
|
|
598
|
+
safeData: paypalWebhookSafeData(type, resource, disputeTransactions, parentCaptureId),
|
|
517
599
|
};
|
|
600
|
+
if (objectKind === 'refund' && (!money || !parentCaptureId)) {
|
|
601
|
+
return {
|
|
602
|
+
status: 'normalization_failed',
|
|
603
|
+
envelope,
|
|
604
|
+
error: {
|
|
605
|
+
code: 'WEBHOOK_OBJECT_INVALID',
|
|
606
|
+
safeMessage: 'The PayPal refund event is missing money or its parent capture identity.',
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (objectKind === 'dispute' && !money) {
|
|
611
|
+
return {
|
|
612
|
+
status: 'normalization_failed',
|
|
613
|
+
envelope,
|
|
614
|
+
error: {
|
|
615
|
+
code: 'WEBHOOK_OBJECT_INVALID',
|
|
616
|
+
safeMessage: 'The PayPal dispute event is missing dispute amount or currency.',
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
518
620
|
return { status: 'normalized', event: normalized };
|
|
519
621
|
}
|
|
520
622
|
class PrismPayPalConnector {
|
|
Binary file
|
package/package.json
CHANGED