PackagesPayment providers

@prood/payment-easypay

Easypay payment provider for Portugal — Multibanco, MB WAY, and credit card.

@prood/payment-easypay implements the PaymentProvider interface for Easypay — a Portuguese payment provider supporting Multibanco references, MB WAY, and credit card.

Installation

Workspace package. Available from the source repo for now.

Usage

ts
import { EasypayPaymentProvider } from '@prood/payment-easypay'

const provider = new EasypayPaymentProvider({
  accountId: config.accountId,
  apiKey: config.apiKey,
  baseUrl: config.baseUrl,
})

Configuration

FieldDescription
accountIdEasypay account ID
apiKeyAPI key
baseUrlAPI base URL. Optional, defaults to live

Each store enters these under Integrations in the dashboard. There is no environment fallback.

Environments

EnvironmentBase URL
Productionhttps://api.prod.easypay.pt
Sandboxhttps://api.test.easypay.pt

Payment methods

MethodFlowUI
MultibancoReference-basedEntity + reference displayed; customer pays at ATM/homebanking
MB WAYPush notificationPhone number input; customer confirms on phone
Credit cardRedirectRedirect to Easypay hosted payment page

Selecting a method

Pass method on createSession() (defaults to multibanco):

methodFlow
multibancoMultibanco reference (entity + reference, 3-day deadline)
mbwayMB WAY push notification; needs a Portuguese mobile number
cardRedirect to Easypay hosted card page
ts
const session = await provider.createSession({
  amount: 29.99,
  currency: 'EUR',
  orderId: 'ord_456',
  method: 'multibanco',
})

Easypay confirms payments asynchronously via notifications. Use confirmSession() after redirect, but treat webhooks as the authoritative completion signal for reference payments.

Refunds

refund() posts to /2.0/refund/{captureId}, using the capture of the payment, with a 20 second deadline. MB WAY and card payments can be refunded this way. Multibanco can't: Easypay needs the shopper's IBAN, so those orders use the manual refund path.

Two guards keep a retry from paying twice:

  • The Idempotency-Key header. Easypay documents it for every POST, not on the refund operation itself, and forgets keys after 24 hours.
  • The capture's status. A capture already marked refunded sends nothing.

transaction_key is only Easypay's internal reference for the refund. It doesn't deduplicate anything.

Easypay's refund notification carries the refund's id, not the payment's, so verifyWebhook reads /2.0/refund/{id} to find the payment and its order. Only a refund with status success changes the order: a value below the capture is payment.partially_refunded, anything else payment.refunded.

Factory usage

ts
import { getTenantPaymentProvider } from '@prood/commerce'

const { provider } = await getTenantPaymentProvider(orgId, 'easypay')

Payment flow

Drag to pan · Scroll to zoom

Webhook setup

Configure in Easypay merchant panel:

URL: https://checkout.example.com/api/webhooks/easypay/{orgId}