Repository navigation
feat: payroll escrow reclaim, webhook incident alerts, dunning workflow builder, multi-asset checkout - #192
Merged
Prudentdev-xyz merged 6 commits intoOct 18, 2026
Conversation
Payroll deposits now live in contract escrow with an expiration_timestamp. Recipients can claim until the deadline; once it passes, the sender can reclaim whatever was never claimed. - create_payroll_escrow: pulls the deposit into the contract and records total_deposited, expiration_timestamp, and an escrow id - claim_payroll_escrow: recipient withdrawals, only while now <= expiration - reclaim_unclaimed_payroll: sender-only, only while now > expiration; marks the escrow reclaimed before transferring unclaimed = total_deposited - claimed_amount, so it can only happen once - emits payroll_escrow_created, payroll_escrow_claimed and payroll_escrow_reclaimed events - get_payroll_escrow read-only lookup Refs Orbit-xyz#188
… email Tracks delivery health per merchant endpoint and alerts merchants when an endpoint starts failing, then sends a recovery notice when it comes back. - webhook-alerts.js: in-memory failure telemetry with a 15-minute rolling failure rate. An incident opens after 5 consecutive 5xx or connection errors and resolves on the next 2xx. - Alerts carry endpoint URL, failed event type, error code and a link to the DLQ inspector. - Channels: Slack Incoming Webhook, PagerDuty Events API v2 (trigger and resolve share a dedup key), and email via ALERT_EMAIL_RELAY_URL. - Merchant routes: GET/POST /webhook-alert-channels and DELETE /webhook-alert-channels/:id, backed by a new webhook_alert_channels table. - webhooks.js: optional delivery observer, called off the request path after every dispatch and resend. Delivery results are unchanged. Refs Orbit-xyz#189
…bscribe Subscribers can pay in an asset other than the plan currency. Merchants approve the routers allowed to convert for them. - approve_swap_router / revoke_swap_router: merchant-controlled allowlist - create_vault_with_swap: one invocation that creates the vault, stores the swap config, and approves the payment-token allowance. Soroban allows a single invocation per transaction, so this is what keeps subscribe and swap atomic. It reverts as a whole on any failure and rejects routers the merchant has not approved. Refs Orbit-xyz#191
…ery funnel Merchants can now define their own recovery schedule for failed payments. - dunning-workflow.js: step schema (day, retry pull, action, grace discount, markdown email templates), sequence validation (strictly increasing days, final attempt last, known placeholders only), and placeholder rendering. - GET/PUT /dunning/workflow store the workflow in dunning_workflows. Without a saved workflow the default Day 1 / 3 / 5 / 7 cadence applies. - processDunningRetries and markSubscriptionPastDue honor a saved workflow for retry days and attempt count. The default path is unchanged. - dunning.attempt_failed webhooks include the rendered email for the step. - GET /dunning/analytics: failed and recovered invoices, recovered volume, net churn prevented, and recovery rate. Refs Orbit-xyz#190
Adds a dashboard page at /dashboard/dunning for configuring recovery steps. - Timeline of steps (day, action, retry toggle, grace discount) with add, remove and select - Step editor with a markdown email body, placeholder chips, and a live preview. The preview escapes HTML before formatting. - Validation before save: increasing days, discount range, unknown placeholders, and a final attempt as the last step - Recovery funnel cards: failed invoices, recovered invoices with volume, net churn prevented, and recovery rate Refs Orbit-xyz#190
…eckout Customers can pay a USDC subscription from another asset in their wallet. - Pay With selector listing the connected wallet's balances for USDC, EURC, XLM and AQUA (AQUA on mainnet only). Assets with no balance are hidden. - Live swap quote from Horizon strict-receive paths: rate, price impact against the mid-market rate (flagged above 5%), and the max input after the chosen slippage tolerance (0.5%, 1%, 3%). - Swap subscribe runs as one Soroban transaction calling create_vault_with_swap, so the vault, swap config and payment allowance settle together or not at all. The USDC preflight is skipped for swaps. - Routers come from plan.swap_router_address, which the merchant approves on-chain. Without an approved router the swap option is blocked. Refs Orbit-xyz#191
|
@Akanimoh12 is attempting to deploy a commit to the Abdqoharr Olayiwola's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Akanimoh12 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements four issues in one branch:
Each change is its own commit, so they can be reviewed separately.
#188 Payroll escrow reclamation (
contracts/soroban)create_payroll_escrowpulls the deposit into the contract and recordstotal_deposited,claimed_amount,expiration_timestamp, and areclaimedflag.claim_payroll_escrowlets the recipient withdraw whilenow <= expiration_timestamp.reclaim_unclaimed_payroll(sender, payroll_id)requires sender auth andnow > expiration_timestamp. It computesunclaimed = total_deposited - claimed_amount, asserts it is positive, marks the escrowreclaimedbefore transferring, and emitspayroll_escrow_reclaimed.#189 Webhook incident alerting (
apps/backend)webhook-alerts.jstracks each endpoint over a 15-minute rolling window and reports the failure rate. An incident opens after 5 consecutive 5xx or connection errors. The next 2xx resolves it and sends a "Webhook Delivery Restored" notice. Other 4xx responses are neutral.dedup_key), and email through a relay set byALERT_EMAIL_RELAY_URL.GET/POST /webhook-alert-channels,DELETE /webhook-alert-channels/:id. Backed by the newwebhook_alert_channelstable.webhooks.jscalls an optional observer after every dispatch and resend. It runs off the request path, so alert delivery never slows or fails a webhook.#190 Dunning workflow builder (
apps/backend+apps/frontend)GET/PUT /dunning/workflowstore a merchant's steps (day, retry toggle, action, grace discount, markdown templates) indunning_workflows. Validation requires increasing days, a final attempt as the last step, and known placeholders only.dunning.attempt_failedwebhooks include the rendered email subject and body for the step, so the merchant can send it.GET /dunning/analyticsreturns failed and recovered invoices, recovered volume, net churn prevented, and recovery rate./dashboard/dunningwith a timeline editor, a markdown editor with placeholder chips and a live preview (escaped before rendering), validation before save, and funnel cards.#191 Multi-asset checkout (
packages/checkout-widget+ contract)strict-receivepaths: rate, price impact against the mid-market rate (flagged above 5%), and the maximum input after slippage (0.5%, 1%, 3%).create_vault_with_swap, creates the vault, stores the swap config, and approves the payment-token allowance in one invocation. Soroban allows one contract invocation per transaction, so this is what keeps the steps atomic. The whole call reverts if any step fails.approve_swap_router/revoke_swap_router. The contract rejects any router the merchant has not approved.Testing
Run and passing:
test/webhook-alerts.test.js,test/webhooks.test.js,test/dunning.test.js, 22 tests passing. This run was before the finaldunning.jschange, so the dunning suite should be re-run.Written but not run:
contracts/soroban/src/test.rsfor escrow (test_payroll_*) and swap subscribe (test_create_vault_with_swap_*). I have not compiled or run them. Runcargo testincontracts/sorobanbefore merging.test/dunning-workflow.test.mjs(Node test runner) has not been run. The view has not been type-checked.package-lock.jsonis out of sync with itspackage.json(npm cifails onutf-8-validate) and that predates this PR. The JSX was not syntax-checked.Known limitations and follow-ups
?view=developers&delivery=<id>. The developers view does not yet open that delivery automatically.swap_router_addressset, but no route exists to set it yet. The contract ABI inpackages/contract-sdkwas not regenerated for the new entrypoints.Closes #188
Closes #189
Closes #190
Closes #191