Custom Provider Integrations — Evoxup
:root{
--bg:#fff;--soft:#f6f8fc;--ink:#0a1222;--muted:#5e6c82;--line:#dfe7f2;
--blue:#1769ff;--blue2:#0f50c1;--blue3:#0a2d70;--red:#d7263d;--dark:#07111f;
--green:#16834c;--amber:#a46c00;--purple:#6847bd;--shadow:0 18px 50px rgba(20,62,125,.10)
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;color:var(--ink);background:var(--bg);line-height:1.72}
a{color:inherit;text-decoration:none}
.container{width:min(1200px,calc(100% - 40px));margin:auto}
h1,h2,h3,h4{line-height:1.13;margin:0 0 16px}
h1{font-size:clamp(3rem,6vw,5.8rem);letter-spacing:-.055em}
h2{font-size:clamp(2rem,4vw,3.6rem);letter-spacing:-.045em}
h3{font-size:1.35rem}
p{margin:0 0 18px;color:var(--muted)}
.lead{font-size:clamp(1.08rem,1.6vw,1.32rem);max-width:900px}
.eyebrow{display:inline-flex;align-items:center;gap:9px;font-size:.76rem;font-weight:900;letter-spacing:.13em;text-transform:uppercase;color:var(--blue)}
.eyebrow:before{content:"";width:28px;height:2px;background:var(--red)}
.btn{display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:0 20px;border-radius:12px;font-weight:850;border:1px solid transparent}
.btn.primary{background:var(--blue);color:#fff}.btn.secondary{background:#fff;border-color:var(--line)}
.badge{display:inline-flex;padding:5px 10px;border-radius:999px;font-size:.68rem;font-weight:900;background:#eaf1ff;color:var(--blue2)}
header{position:sticky;top:0;z-index:20;background:rgba(255,255,255,.93);backdrop-filter:blur(16px);border-bottom:1px solid var(--line)}
.nav{height:72px;display:flex;align-items:center;justify-content:space-between;gap:20px}
.brand{display:flex;align-items:center;gap:10px;font-weight:900}
.brandmark{width:34px;height:34px;border-radius:11px;background:linear-gradient(145deg,var(--blue),var(--blue3));position:relative}
.brandmark:after{content:"";position:absolute;width:10px;height:10px;border-radius:50%;background:var(--red);right:-3px;bottom:4px;border:3px solid white}
.navlinks{display:flex;gap:22px;font-size:.92rem;font-weight:750;color:#33445e}
.navright{display:flex;gap:10px}
.hero{padding:100px 0 72px;background:
radial-gradient(circle at 84% 8%,rgba(23,105,255,.14),transparent 28%),
radial-gradient(circle at 8% 28%,rgba(215,38,61,.06),transparent 20%),
linear-gradient(180deg,#fff,#f8fbff)}
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:54px;align-items:center}
.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:26px}
.console{background:linear-gradient(150deg,#07111f,#102b60);border-radius:28px;padding:28px;color:#fff;box-shadow:0 30px 80px rgba(7,17,31,.24)}
.console p{color:#b8c8df}
.consolebox{background:#091728;border:1px solid #263f67;border-radius:16px;padding:16px;margin-top:18px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.83rem;color:#d9e7ff}
.consoleline{padding:6px 0;border-bottom:1px solid rgba(255,255,255,.06)}.consoleline:last-child{border-bottom:0}
.k{color:#88b7ff}.v{color:#cfe1ff}.ok{color:#71d39a}
section{padding:90px 0}.soft{background:var(--soft)}.dark{background:linear-gradient(135deg,#07111f,#0a1e3c);color:#fff}
.dark h2,.dark h3,.dark h4{color:#fff}.dark p{color:#aebdd2}
.content-layout{display:grid;grid-template-columns:270px 1fr;gap:38px;align-items:start}
.toc{position:sticky;top:95px;border:1px solid var(--line);border-radius:18px;background:#fff;padding:16px}
.toc strong{display:block;font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:#8090a8;padding:7px 9px}
.toc a{display:block;padding:9px 10px;border-radius:9px;color:#465872;font-weight:700;font-size:.88rem}
.toc a:hover{background:#edf3ff;color:var(--blue)}
.article{min-width:0}.article>section{padding:0 0 72px}.article h2{font-size:2.35rem}.article h3{margin-top:28px}
.callout{padding:18px 20px;border-left:4px solid var(--blue);background:#f1f6ff;border-radius:10px;color:#42536d;margin:20px 0}
.warn{border-left-color:var(--red);background:#fff3f5;color:#7d3340}
.good{border-left-color:var(--green);background:#eefaf3;color:#37634b}
.tablewrap{overflow:auto;border:1px solid var(--line);border-radius:18px;background:#fff;margin:20px 0}
table{width:100%;border-collapse:collapse;min-width:900px}
th,td{padding:14px 16px;border-bottom:1px solid #edf1f7;text-align:left;vertical-align:top}
th{background:#f7f9fd;font-size:.77rem;text-transform:uppercase;letter-spacing:.05em;color:#40516a}
td{color:#52627a;font-size:.92rem}
.arch{padding:24px;border-radius:20px;background:#091523;color:#fff;margin:22px 0}
.archgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;align-items:center}
.anode{padding:16px 10px;border:1px solid #29466f;border-radius:13px;background:#0f213c;text-align:center}
.anode strong{display:block;font-size:.87rem}.anode small{color:#9ab0cf;font-size:.72rem}
.arrow{text-align:center;font-weight:900;color:#8ba2c5}
.matrix{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin-top:18px}
.box{padding:22px;border:1px solid var(--line);border-radius:18px;background:#fff}
.box strong{display:block;margin-bottom:7px}
.code{background:#0b1524;color:#d9e7ff;border-radius:14px;padding:18px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.87rem;white-space:pre-wrap}
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:20px}
.step{padding:20px;border:1px solid var(--line);border-radius:17px;background:#fff}
.stepnum{width:38px;height:38px;border-radius:11px;background:#edf3ff;color:var(--blue);display:grid;place-items:center;font-weight:900;margin-bottom:13px}
.scenario{border:1px solid var(--line);border-radius:20px;background:#fff;margin:22px 0;overflow:hidden}
.scenario-head{padding:18px 22px;background:#f8faff;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:15px;align-items:center}
.scenario-body{padding:22px}
.flowline{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin:18px 0}
.flowitem{padding:15px;border:1px solid var(--line);border-radius:13px;background:#fff;text-align:center}
.flowitem strong{display:block;font-size:.9rem}.flowitem small{color:#7b899f}
.security-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.sec{padding:22px;border-radius:18px;background:#0c1b32;border:1px solid #1d3457}
.sec strong{display:block;color:#fff;margin-bottom:7px}.sec p{margin:0}
.fields{display:grid;grid-template-columns:repeat(2,1fr);gap:18px}
.fieldcard{padding:22px;border:1px solid var(--line);border-radius:18px;background:#fff}
.fieldcard code{background:#edf3ff;color:var(--blue2);padding:2px 6px;border-radius:6px}
.faq details{border:1px solid var(--line);border-radius:14px;padding:17px 19px;background:#fff;margin-bottom:10px}
.faq summary{font-weight:850;cursor:pointer}.faq p{margin:12px 0 0}
.cta{padding:82px 0;background:linear-gradient(135deg,#07111f,#102b5c);color:#fff}
.cta h2{color:#fff}.cta p{color:#bccbe0}
footer{background:#050b14;color:#9fb0c8;padding:52px 0 25px}
.footgrid{display:grid;grid-template-columns:1.4fr repeat(4,1fr);gap:28px}
footer h4{color:#fff;margin:0 0 12px}footer a{display:block;margin:7px 0;font-size:.88rem}
.footnote{border-top:1px solid #172238;margin-top:30px;padding-top:18px;font-size:.8rem;color:#75859e}
@media(max-width:980px){
.navlinks{display:none}.hero-grid,.content-layout{grid-template-columns:1fr}.toc{position:static}.archgrid{grid-template-columns:1fr}.arrow{transform:rotate(90deg)}
.matrix,.fields{grid-template-columns:1fr}.steps,.security-grid{grid-template-columns:repeat(2,1fr)}.flowline{grid-template-columns:1fr}.footgrid{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:640px){
.container{width:min(100% - 26px,1200px)}h1{font-size:3rem}section{padding:68px 0}.steps,.security-grid{grid-template-columns:1fr}.footgrid{grid-template-columns:1fr}.navright a:first-child{display:none}
}
img,svg{display:block;max-width:100%}
.brand-mark svg{width:23px;height:23px;color:#fff}
.brand-mark{
width:38px;height:38px;border-radius:12px;
background:linear-gradient(145deg,var(--blue),var(--teal));
display:grid;place-items:center;box-shadow:0 8px 24px rgba(18,84,244,.24);
}
Custom Provider Solution
Connect a Commerce Provider Evoxup Has Never Seen Before.
Define how the provider sends events, how Evoxup authenticates, how transactions are verified, how products are identified, and how provider data is normalized into the same internal fulfillment pipeline used by every other integration.
CUSTOM PROVIDER
Describe the Provider. Keep Core Stable.
The custom integration layer translates external behavior into Evoxup's internal event model.
transport: webhook + api
auth: bearer token
verification: api_lookup
product_id_path: data.product.id
transaction_id_path: data.order.id
mapping: enabled
Definition
What a Custom Provider Is — and Is Not
A Custom Provider is a provider-neutral integration definition for a commerce platform that does not have a ready-made Evoxup profile.
It tells Evoxup how to communicate with the provider without changing the Core membership, licensing or entitlement architecture.
Custom Provider is responsible for
Transport, credentials, verification, event parsing, product identity and provider-specific API behavior.
Core remains responsible for
Internal products, mapping, membership, license authority, entitlement, duplicate protection and fulfillment records.
Core principle: adding a new provider should extend the integration boundary, not create a second membership or licensing system.
Architecture
The Custom Provider Sits Before the Internal Business Logic
ProviderWebhook/API
→
AuthenticateCredential layer
→
VerifyTrust policy
→
NormalizeInternal event
↓
Identify ProductExternal ID
→
MapEVO Product
→
FulfillMembership / License
→
RecordResult / duplicate guard
This separation is what allows Evoxup to support additional providers without embedding every provider's payload format into the fulfillment engine.
Integration Contract
Define the Provider Before Writing Business Rules
A robust Custom Provider configuration should describe the provider's behavior explicitly.
Provider identityInternal slug, display name and environment information.
provider_slug
Webhook endpointWhere the external platform sends sale or order events.
webhook_path
API base URLBase endpoint for verification, discovery or synchronization.
api_base_url
Authentication typeBearer token, API key, Basic auth or another supported mechanism.
auth_mode
Verification policySignature, HMAC, API lookup, authenticated callback or another supported policy.
verification_mode
Product identifier pathWhere the provider's stable product ID exists inside the normalized event source.
product_id_path
Transaction identifier pathStable sale/order identifier used for tracking and duplicate protection.
transaction_id_path
Customer fieldsEmail, name and other fields required for customer fulfillment.
customer_email_path
A provider contract should describe observable technical behavior — not marketing text about the provider.
Transport
Webhook, API, or Both?
| Model | Use It When | Strength | Limitation |
| Webhook only | Provider pushes complete transaction data and offers sufficient authenticity controls. | Fast event delivery | May lack independent transaction lookup |
| Webhook + API verification | Provider pushes an event but offers a trusted API to confirm the order. | Strong separation between notification and verification | Requires API credential and provider availability |
| API polling / lookup | No webhook exists or the workflow is manually triggered. | Direct provider query | Not ideal for instant fulfillment |
| WordPress-native event | The sale already happens inside WordPress. | Direct application-level integration | Specific to the local commerce source |
Transport answers how data arrives. It does not automatically answer whether that data is trustworthy.
Authentication
How Evoxup Authenticates to the Provider
| Authentication | Typical Configuration | Where Used |
| Bearer Token | Authorization: Bearer … | API calls, discovery, transaction lookup |
| API Key Header | X-API-Key: … | Provider APIs using custom headers |
| Basic Authentication | Username + password/token | Legacy or provider-specific APIs |
| Query Credential | Key included in query parameters | Only when required by provider design |
| No outbound auth | None | Webhook-only providers without API access |
Credentials should be stored as secrets and masked in the UI. A saved secret should not be rendered back into the browser simply to show that it exists.
Verification Policy
Do Not Force Every Provider Into HMAC
Verification must match the provider's real capability.
| Verification Method | How It Works | Best Fit |
| Signature / HMAC | Compute or validate a signature using the provider signing secret and raw payload. | Providers with signed webhooks |
| Outbound API verification | Use the transaction ID from the event to retrieve the order directly from the provider. | Providers with trusted order APIs |
| Authenticated callback token | Validate a provider-specific token or secret included in the callback. | Platforms with simple shared-secret callbacks |
| IP / network restriction | Restrict origin where the provider publishes stable network ranges. | Supplementary control, not universally available |
| No native verification | Use the strongest feasible policy without pretending a signature exists. | Legacy or limited providers |
Important: “test” and “live” are transaction context. They are not substitutes for authenticity verification.
Normalization
Convert Every Provider Into One Internal Event Shape
Provider payloads differ. The Core should not care whether one provider says sale_id and another says order.data.id.
{
"provider": "custom_vendor",
"transaction_id": "ORD-104992",
"event_type": "sale.completed",
"test_mode": false,
"product_id": "prod_8731",
"variant_id": "variant_pro",
"customer": {
"email": "customer@example.com",
"name": "Customer Name"
},
"amount": 4900,
"currency": "USD",
"verified": true
}
Once the event is normalized, Product Mapping and fulfillment can operate without knowing the provider's original JSON structure.
Normalization is the boundary that prevents provider-specific field names from leaking into membership and licensing logic.
Product Discovery
Discovery Is Optional — but Valuable When the Provider Offers It
1
Test ConnectionConfirm credentials and API accessibility.
2
List ProductsRetrieve stable external IDs and provider metadata.
3
Store Discovery StatePresent discovered items without creating mappings automatically.
4
Map IntentionallyAdministrator assigns the correct internal destination.
If the provider does not offer a catalog API, manual mapping can still work as long as the external product identifier from transactions is stable.
Mapping & Sync
Custom Providers Can Participate in the Same Extensions
| Capability | What the Custom Provider Must Supply | What Evoxup Does |
| Product Mapping | Stable external product ID | Routes the product to EVO Product / plan / supported target |
| Product Sync | Catalog API and provider metadata fields | Refreshes name, status, price, URL, variants where supported |
| Transaction fulfillment | Verified normalized sale event | Runs Core membership/licensing/entitlement pipeline |
| Duplicate protection | Stable transaction identifier | Prevents repeated fulfillment of the same sale |
Testing
Test Events Should Exercise the Real Pipeline
A useful integration test should not bypass the exact steps that matter in production.
ReceiveTest event
VerifySame trust policy
MapReal route
FulfillControlled test outcome
RecordTraceable result
The test marker should exist for traceability and policy decisions. It should not silently disable verification or route through a completely different fulfillment engine.
Security
Secrets, Signatures and Boundaries
Mask SecretsShow “Saved securely” rather than rendering raw credentials.
Replace ExplicitlyUse a dedicated Replace Credential action to change saved secrets.
Verify Raw PayloadSignature schemes often require the exact raw body before parsing.
Separate Trust from MappingA known product ID does not prove the event is authentic.
Never use product matching, test mode, or the presence of a customer email as proof that an inbound webhook is legitimate.
Examples
Three Common Custom Provider Patterns
Pattern A — Signed Webhook ProviderWebhook
The provider sends the full sale payload and an HMAC signature.
POST /evoxup/webhook/custom-vendor
X-Signature: 7f9a...
1. Read raw body
2. Validate HMAC
3. Parse JSON
4. Normalize transaction
5. Resolve product mapping
6. Fulfill
7. Store transaction result
Pattern B — Unsigned Webhook + API VerificationWebhook + API
The event itself is only a notification. Evoxup retrieves the order directly from the provider before fulfillment.
Webhook says: order_id = 104992
↓
GET /api/orders/104992
Authorization: Bearer ****
↓
Provider API confirms:
status = paid
product_id = prod_8731
customer = customer@example.com
↓
Normalize → Map → Fulfill
Pattern C — No Product Discovery APIManual Mapping
The provider sends a stable product code in each transaction but does not expose a catalog endpoint.
transaction.product_code = "PRO-ANNUAL"
Manual route:
PRO-ANNUAL → EVO Product #42 → PRO membership policy
Product Mapping still works because routing only requires a stable external identifier.
Failure Handling
What Should Happen When Something Goes Wrong
| Failure | Expected Result | Do Not Do |
| Invalid signature | Reject event and log verification failure. | Do not map or fulfill. |
| API verification returns unpaid | Reject or hold according to policy. | Do not create paid access. |
| Unknown external product | Record unmapped transaction / require mapping. | Do not guess by product name. |
| Duplicate transaction ID | Ignore repeated fulfillment. | Do not issue a second membership/license. |
| API unavailable | Return retriable/verification failure state as appropriate. | Do not silently treat failure as verified. |
| Credential missing | Block API-dependent operation and show clear configuration error. | Do not expose internal secrets in the error. |
FAQ
Custom Provider Questions
Does every provider need a ready-made Evoxup profile?
No. Ready-made profiles are guided shortcuts for known providers. Custom Provider exists so the architecture is not limited to that list.
Does every provider need a webhook signature?
No. Use the strongest verification mechanism the provider actually supports. Some providers are better verified through their API.
Can a Custom Provider use Product Mapping?
Yes, as long as transactions expose a stable external product identifier.
Can Product Sync work with a Custom Provider?
Yes when the provider exposes a suitable catalog API and the adapter defines how product metadata should be retrieved.
Can I create a provider using only marketing documentation?
No. A reliable integration needs the provider's actual webhook/API contract, authentication rules, payload fields and verification capabilities.
Should a Custom Provider create memberships directly?
No. It should normalize the verified commerce event and hand it to the same Core fulfillment pipeline used by other integrations.
Design Principle
A New Provider Should Add Translation — Not New Business Logic.
The integration knows the provider. Core knows the product, membership, licensing and entitlement rules. Keeping those responsibilities separate is what makes provider-neutral commerce maintainable.
Connect Your Provider
Webhook, API or Both — Bring the Provider Into the Same Evoxup Pipeline.
Define the technical contract once, then reuse Evoxup's product mapping, fulfillment, licensing and entitlement architecture.