Paymos for Easy Digital Downloads

Description

The official package contains no merchant credentials. Open Downloads -> Settings -> Payments -> Paymos and click Connect Paymos. Approve the current project in Paymos; the plugin receives Sandbox and Live credentials once, stores them in an AES-256-GCM encrypted WordPress option, and discards the short-lived OAuth token.

Runtime Merchant API requests remain HMAC signed. Signed webhooks update invoices without a local invoice reconciliation job: a delivery that fails is retried by Paymos across roughly 16 hours, and anything that outlives the ladder is replayed from the Paymos dashboard. Pending refunds are reconciled locally by a 1-minute WP-Cron job (paymos_edd_refunds_reconcile).

External services

Paymos handles payment requests for this Easy Digital Downloads gateway. To connect, click Connect Paymos in your WordPress admin and approve the connection in Paymos. Device authorization starts at https://app.paymos.io/connect/cms/device; the plugin polls https://app.paymos.io/connect/token for approval. The browser approval URL carries the plugin identifier, store origin, admin return URL, and user_code. The plugin sends the secret device_code to the token endpoint. It stores the returned Sandbox and Live credentials in AES-256-GCM-encrypted WordPress options and discards the short-lived OAuth token.

For payments, the plugin creates invoices at https://api.paymos.io/v1/invoices, checks them with GET /v1/invoices/{invoice_id}, and cancels them with POST /v1/invoices/{invoice_id}/cancel. Each signed request sends a key ID, timestamp, and HMAC signature. The API secret stays with the plugin for signing. Invoice fields include project_id, amount, currency, external_order_id, and, when available, the native customer ID as client_id. New orders use a random opaque reference. Existing references remain in use and can contain an EDD purchase key.

For refunds, the plugin sends POST /v1/refunds requests to https://api.paymos.io/v1/refunds with only invoice_id, mode (full or overpayment), and external_order_id; Paymos returns funds strictly to the verified first_sender address recorded on the invoice. The plugin checks individual refund status with GET /v1/refunds/{refund_id}, polls cursor-paginated refund pages with GET /v1/refunds (cursor, limit, invoice_id, and status), and in Sandbox mode simulates terminal outcomes with POST /v1/sandbox/refunds/{refund_id}/simulate sending status (completed, failed, or cancelled). Pending refunds are polled by a 1-minute WP-Cron job (paymos_edd_refunds_reconcile).

Buyers pay over HTTPS at https://checkout.paymos.io. Signed invoice and payment webhooks return to the store to update orders. Service terms and data handling appear in the Terms and Privacy Policy.

Installation

  1. Install and activate the official release.
  2. Open the intended project in Paymos; that current project is used automatically.
  3. Open Downloads -> Settings -> Payments -> Paymos.
  4. Click Connect Paymos and approve the displayed store URL and current project.
  5. Test in Sandbox, then switch to Live.

FAQ

Does the ZIP contain secrets?

No. Every merchant downloads the same public package.

Where are credentials stored?

In a non-autoloaded WordPress option encrypted with AES-256-GCM using installation security salts.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Paymos for Easy Digital Downloads” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.6.2

  • fix(cms): align marketplace manifests, refund copy, store links, and release guards
  • chore: bundle Paymos PHP SDK v1.9.1
  • chore: rebuild canonical CMS package

1.6.1

  • fix(plugins): use wp_add_inline_script, sanitize nonces before wp_verify_nonce, and guard curl_close on PHP 8+
  • chore: bundle Paymos PHP SDK v1.9.1

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.6.0

  • feat(refunds): finalize clean merchant refunds spec, SDKs, CMS plugins, and targeted test scripts
  • wip: snapshot shared working tree before merging main
  • wip(refunds): merchant refund plugin work (CMS adapters, PHP SDK, tests) – unverified WIP snapshot
  • chore: bundle Paymos PHP SDK v1.9.0
  • chore: rebuild canonical CMS package

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.5.4

  • chore: bundle Paymos PHP SDK v1.8.1

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.5.3

  • chore: rebuild canonical CMS package

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.5.2

  • fix(edd): validate refund controls and prepare directory database queries

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.5.1

  • fix(plugins): require helper-complete PHP SDK for refunds

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.5.0

  • fix(ton): return friendly addresses with existing webhook schema
  • feat(edd): native refund adapter for full and overpayment refunds
  • fix(edd): refuse native refunds that bypass the Paymos gateway
  • fix(edd): prepare clean WordPress.org distribution
  • chore: bundle Paymos PHP SDK v1.8.0
  • chore: rebuild canonical CMS package

  • Moved optional Paymos diagnostics from PHP error_log to the EDD debug log, with masking for supported key patterns. Logging remains off by default.

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.4.10

  • chore: rebuild canonical CMS package

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.

1.4.9

  • chore: rebuild canonical CMS package

  • Compare amounts as exact decimals and require complete invoice snapshots plus API confirmation before changing payment state.

  • Serialize order updates and recover when native payment completion returns false from save or status updates.
  • Check ownership when updating event reservations, and purge at most 100 expired records per reservation.

Fixed:
* Entries that were present, non-empty and still English — Connect Paymos in
German and Spanish, the plugin name in Turkish and Chinese, Webhook URL in
Chinese — and one string missing from every catalogue
(in the invoice currency, the fallback in the underpayment notice).
* A late non-final webhook could reopen a finished order. Webhooks are
delivered at least once and in no particular order, and only paid orders were
guarded: an invoice.underpaid_waiting or invoice.confirming arriving after
the invoice had already ended underpaid, expired or cancelled moved the order
back into an open state. Nothing leaves a final status on the server, so once
one is recorded for an invoice every later event for it is ignored and the
final status stays recorded.
* A new invoice now resets the Paymos status recorded on the payment, so an
event for it is never taken for a stale one after an earlier final status.
* A webhook retry that arrived while the first delivery was still being
processed was answered 200 “duplicate”. Paymos gives a delivery 10 seconds and
retries, while a slow reverse-verification call can take longer; the retry was
acknowledged as delivered, and if the first attempt then failed the event was
lost. An event that is only locked, not yet committed, is now answered 409 so
Paymos tries again, and the lock the first delivery holds is left alone.