IP matching is required to go live with the on-ramp widget. Build and test
your integration in sandbox first. MoonPay confirms enforcement is active on
your account before you go live.
Requirements
A widget URL passes IP matching when both of the following are true:- The URL is signed with your secret key. See URL signing.
- The URL includes an
allowedIpAddressparameter set to a hash of the customer’s IP address, and that hash is part of the signed query string.
Before you begin
- Get your secret key from the Developers > API Keys page of your MoonPay dashboard. Keep it on your backend and never expose it client-side.
- Make sure your backend can capture the customer’s live, public IP address at the point of URL generation.
How it works
Generate a signed URL with IP matching
Build the URL on your backend in two steps: hash the customer’s IP address and add it asallowedIpAddress, then sign the full query string.
Step 1: Hash the customer’s IP address
Capture the customer’s public IP address, hash it with your secret key using HMAC-SHA256, and append the result to the widget URL as theallowedIpAddress parameter.
Node.js
- Use the IP address of the device that will open the widget, such as the
True-Client-IPheader or the left-mostX-Forwarded-Forvalue behind your proxy or CDN. - Do not use a server-side or internal address.
- Canonicalize the IP address to a stable string before hashing. HMAC is sensitive to any formatting difference, so the value you hash must exactly match the value MoonPay observes.
Step 2: Sign the URL
Sign the full query string, includingallowedIpAddress, with the same secret key. Append the result as the signature parameter.
Node.js
What to expect
When the IP address does not match, the customer sees an “Unverified Connection” error in place of the widget:
Unverified Connection We couldn’t verify this request. If this continues, contact support through the official MoonPay app or website.
iCloud Private Relay
iCloud Private Relay is an iCloud+ feature, on by default for iCloud+ subscribers, that routes Safari traffic through an Apple relay. When a customer with Private Relay opens the widget in a Safari-based view, MoonPay observes an Apple relay egress IP address instead of the customer’s real IP address. Your backend captures the real IP address from your app’s own traffic, so the two never match and the widget fails to load for every Private Relay user. Whether your integration is affected depends on how you open the widget:
MoonPay recommends a WebView on iOS and Android, so integrations that follow the integration design guide are not affected. Handle Private Relay only if you open the widget in
SFSafariViewController, for example for Apple Pay.
Handle Private Relay sessions
Detect Private Relay before you generate the widget URL, and omitallowedIpAddress from the signed URL when it is active. MoonPay skips IP enforcement for that session but still validates the signature, so the URL cannot be tampered with. In this configuration, you assume responsibility for verifying the customer’s IP address.
To detect Private Relay, compare two observations of the customer’s IP address:
- The IP address your backend observes from your app’s own HTTPS traffic. This traffic bypasses the relay.
- The IP address your backend observes from a request made inside the Safari view, for example a lightweight endpoint the view loads once and that redirects back to your app with the observed IP address.
allowedIpAddress as usual. If they differ, omit it:
Node.js
Testing
Test your implementation in sandbox before you turn it on in production, using your sandbox API key from the Developers > API Keys page of your MoonPay dashboard.Next steps
- Test your implementation in sandbox and confirm the widget loads for matching IP addresses and returns an error for mismatches.
- Notify your MoonPay integration contact once testing is complete.
- MoonPay confirms enforcement is active on your account before you go live.