Build a New Provider
Overview
ZKP2P is an open and permissionless protocol. We've now made it very easy for any developer around the world to get started building a new payment integration on ZKP2P. This guide explains how to create provider templates for the ZKP2P PeerAuth extension and integrate new payment platforms.
To build a new integration for your local payment platform, you will need to implement:
- A zkTLS provider template to generate a proof of payment
If you have any questions please do not hesitate to contact us on Telegram
Developer Quickstart
To get started building a new provider:
- Clone the providers repo
- Run
yarn installandyarn start. App is hosted on http://localhost:8080 - Install the PeerAuth extension in your browser
- Create a new directory and JSON file and add the necessary provider data for your integration
- Test your integration by going to developer.zkp2p.xyz
- Click on Open Settings on the page and set Base URL to
http://localhost:8080/. Any changes to your JSON will now be reflected in the extension and developer app - Update
actionType,platform, and setproofEngineto"reclaim". The path to your provider islocalhost:8080/{platform_name}/{provider_name}.json - Click Authenticate to extract metadata (if your template defines
userInput, click a transaction when prompted) - If successful, proceed to Prove a specific transaction
1. Build a zkTLS Provider Template
Getting Started
- Inspect network tab in Dev Tools after logging into your payment website. Or turn on Intercepted Requests in ZKP2P sidebar
- Find a request that contains amount, timestamp/date, recipient ID at a minimum. Look for additional params such as status (to see if payment finalized), currency (if platform supports more than 1 currency)
- A tip is to look for where the transactions page is. Sometimes the transactions are expandable so you can log those too
- Based on the request, populate the template
Configuration Structure
{
"actionType": "transfer_venmo",
"proofEngine": "reclaim",
"authLink": "https://account.venmo.com/?feed=mine",
"url": "https://account.venmo.com/api/stories?feedType=me&externalId={{SENDER_ID}}",
"method": "GET",
"skipRequestHeaders": [],
"body": "",
"metadata": {
"platform": "venmo",
"urlRegex": "https://account.venmo.com/api/stories\\?feedType=me&externalId=\\S+",
"method": "GET",
"shouldSkipCloseTab": false,
"userInput": {
"promptText": "Select a transaction on the page",
"transactionXpath": "//div[contains(@class,'transaction-row')]"
},
"transactionsExtraction": {
"transactionJsonPathListSelector": "$.stories"
}
},
"paramNames": ["SENDER_ID"],
"paramSelectors": [{
"type": "jsonPath",
"value": "$.stories[{{INDEX}}].title.sender.id",
"source": "responseBody"
}],
"secretHeaders": ["Cookie"],
"responseMatches": [{
"type": "regex",
"value": "\"amount\":\"-\\$(?<amount>[^\"]+)\""
}],
"responseRedactions": [{
"jsonPath": "$.stories[{{INDEX}}].amount",
"xPath": ""
}],
"mobile": {
"actionLink": "venmo://paycharge?txn=pay&recipients={{RECEIVER_ID}}¬e=cash&amount={{AMOUNT}}"
}
}
Field Descriptions
Basic Configuration
actionType (required)
- Type:
string - Description: Identifier for the action type (e.g., "transfer_venmo", "receive_payment")
- Example:
"transfer_venmo"
authLink (required)
- Type:
string - Description: URL for user authentication/login page
- Example:
"https://venmo.com/login"
url (required)
- Type:
string - Description: API endpoint URL for the main request
- Example:
"https://api.venmo.com/v1/payments"
method (required)
- Type:
string - Description: HTTP method for the request
- Values:
"GET","POST","PUT","PATCH" - Example:
"POST"
skipRequestHeaders (optional)
- Type:
string[] - Description: Headers to exclude from the notarized request
- Example:
["User-Agent", "Accept-Language"]
body (optional)
- Type:
string - Description: Request body template (for POST/PUT requests)
- Example:
"{\"amount\": \"{{AMOUNT}}\", \"recipient\": \"{{RECIPIENT}}\"}"
proofEngine (required for new templates)
- Type:
string - Description: Selects the proof/attestation engine. For all new providers, set this to
"reclaim". - Values:
"reclaim"(required today)."legacy"remains for backward compatibility only. Additional vendors will be supported in future releases and documented here when available.
Metadata Configuration
metadata (required)
- Type:
object - Description: Configuration for request matching and transaction extraction
"metadata": {
"shouldReplayRequestInPage": false,
"shouldSkipCloseTab": false,
"platform": "venmo",
"urlRegex": "https://api\\.venmo\\.com/v1/payments/\\d+",
"method": "GET",
"fallbackUrlRegex": "https://api\\.venmo\\.com/v1/transactions",
"fallbackMethod": "GET",
"preprocessRegex": "window\\.__data\\s*=\\s*({.*?});",
"userInput": {
"promptText": "Select a transfer below to proceed with authentication",
"transactionXpath": "//section[@id='feed-pending-module']//div[contains(@class,'q1hbnk3w') and contains(@class,'q1hbnk5w')]"
},
"transactionsExtraction": {
"transactionJsonPathListSelector": "$.data.transactions",
"transactionJsonPathSelectors": {
"recipient": "$.target.username",
"amount": "$.amount",
"date": "$.created_time",
"paymentId": "$.id",
"currency": "$.currency"
},
"transactionXPathListSelector": "",
"transactionXPathSelectors": {}
},
"proofMetadataSelectors": [
{
"type": "jsonPath",
"value": "$.data.user.id"
}
]
}
Metadata Fields
shouldSkipCloseTab(optional): When set totrue, prevents the extension from automatically closing the authentication tab after successful authenticationshouldReplayRequestInPage(optional): When set totrue, replays the request in the page context instead of making it from the extension
userInput (optional)
- Type:
object - Description: Prompts the user to click a transaction element before interception. Useful for SPAs or feeds where a detail request is made after a click.
Example:
"userInput": {
"promptText": "Select a transfer below to proceed with authentication",
"transactionXpath": "//section[@id='feed-pending-module']//div[contains(@class,'q1hbnk3w') and contains(@class,'q1hbnk5w')]"
}
Fields:
promptText: Short instruction shown in-page by the extension.transactionXpath: XPath that selects the clickable transaction nodes. The clicked item determines{{INDEX}}for selectors and extraction.
Notes:
- Pair with
shouldReplayRequestInPage: truefor apps that require page context (e.g., N26). - Prefer stable attributes/ids over volatile CSS class names when possible.
Parameter Extraction
paramNames (required)
- Type:
string[] - Description: Names of parameters to extract
- Example:
["transactionId", "amount", "recipient"]
paramSelectors (required)
- Type:
ParamSelector[] - Description: Selectors for extracting parameter values
interface ParamSelector {
type: 'jsonPath' | 'regex' | 'xPath';
value: string;
source?: 'url' | 'responseBody' | 'responseHeaders' | 'requestHeaders' | 'requestBody';
}
Parameter Source Options
The source field in paramSelectors specifies where to extract the parameter from.
responseBody(default): Extract from the response bodyurl: Extract from the request URLresponseHeaders: Extract from response headersrequestHeaders: Extract from request headersrequestBody: Extract from the request body (for POST/PUT requests)
Example:
{
"paramNames": ["userId", "transactionId", "amount"],
"paramSelectors": [
{
"type": "regex",
"value": "userId=([^&]+)",
"source": "url"
},
{
"type": "jsonPath",
"value": "$.data.transactions[{{INDEX}}].id",
"source": "responseBody"
},
{
"type": "xPath",
"value": "normalize-space(//div[@class='tx-amount'][{{INDEX}} + 1])",
"source": "responseBody"
},
{
"type": "regex",
"value": "X-Transaction-Amount: ([0-9.]+)",
"source": "responseHeaders"
}
]
}
Security Configuration
secretHeaders (optional)
- Type:
string[] - Description: Headers containing sensitive data (e.g., auth tokens)
- Example:
["Authorization", "Cookie"]
Response Verification
responseMatches (required)
- Type:
ResponseMatch[] - Description: Patterns to verify in the response
"responseMatches": [
{
"type": "jsonPath",
"value": "$.data.transactions[{{INDEX}}].id",
"hash": false
},
{
"type": "regex",
"value": "\"status\":\\s*\"completed\"",
"hash": true
}
]
responseRedactions (optional)
- Type:
ResponseRedaction[] - Description: Data to redact from the response for privacy
"responseRedactions": [
{
"jsonPath": "$.data.user.email"
},
{
"xPath": "//span[@class='ssn']"
}
]
Mobile SDK Configuration
mobile (optional)
- Type:
object - Description: Special configurations for the ZKP2P mobile SDK
"mobile": {
"includeAdditionalCookieDomains": [],
"actionLink": "venmo://paycharge?txn=pay&recipients={{RECEIVER_ID}}¬e=cash&amount={{AMOUNT}}",
"isExternalLink": true,
"appStoreLink": "https://apps.apple.com/us/app/venmo/id351727428",
"playStoreLink": "https://play.google.com/store/apps/details?id=com.venmo"
}
Fields:
includeAdditionalCookieDomains: Array of additional cookie domains to includeactionLink: Deep link URL for the mobile app with placeholders for dynamic valuesisExternalLink: Boolean indicating if the action link is externalappStoreLink: iOS App Store URL for the appplayStoreLink: Google Play Store URL for the app
Transaction Extraction
Using JSONPath (for JSON responses)
{
"transactionsExtraction": {
"transactionJsonPathListSelector": "$.data.transactions",
"transactionJsonPathSelectors": {
"recipient": "$.target.username",
"amount": "$.amount",
"date": "$.created_time",
"paymentId": "$.id",
"currency": "$.currency"
}
}
}
Using XPath (for HTML responses)
{
"transactionsExtraction": {
"transactionXPathListSelector": "//table[@id='transactions']//tr[contains(@class,'row')]",
"transactionXPathSelectors": {
"amount": "normalize-space(.//td[contains(@class,'amount')])",
"recipient": "normalize-space(.//td[contains(@class,'recipient')])",
"date": "normalize-space(.//td[contains(@class,'date')])",
"paymentId": "normalize-space(.//@data-payment-id)"
}
}
}
Best Practices
-
URL Regex Patterns
- Escape special characters:
\\.for dots - Use specific patterns to avoid false matches
- Test regex patterns thoroughly
- Escape special characters:
-
Parameter Extraction
- Use JSONPath for structured JSON data
- Use XPath for HTML responses
- For regex usage in
paramSelectors/responseMatches, always specify capture groups() - Specify
sourcewhen extracting from non-default locations
-
Security
- List all sensitive headers in
secretHeaders - Use
responseRedactionsto remove PII - Never expose authentication tokens in
responseMatches
- List all sensitive headers in
-
Error Handling
- Provide fallback URLs when primary endpoints might fail
- Use preprocessing regex for embedded JSON data
- Test extraction selectors with various response formats
-
Performance
- Minimize the number of
responseMatchesfor faster verification - Use specific JSONPath expressions instead of wildcards
- Consider response size when designing redactions
- Minimize the number of
Common Issues
- Authenticate does not open desired auth link: Check the Base URL you have set in the extension. Ensure you are running the server which is hosted in port 8080
- Authenticated into your payment platform but not redirected back to developer.zkp2p.xyz: There is an issue with the urlRegex for metadata extraction. Double check your regex is correct
- Metadata returned to app, but Prove fails: There is an issue with the response redactions or headers for the server call. Check your response redactions parameters and server headers
- Parameters not extracted correctly: Check the
sourcefield in yourparamSelectors. By default, parameters are extracted from responseBody
Contributing
We want to make this the largest open source repository of provider templates for global payment platforms. Please open a PR in the providers repository when you have created and tested your template!