The Off-Site Gateway API is a way to seamlessly pay for goods or services online utilizing Dwolla's API. Customers can authenticate and authorize payment of a purchase order utilizing Dwolla's API as the processing platform. Once complete, the user is sent back to the merchant's URL. There are two available workflows to implement Off-Site Gateway:
Submit a form that contains the checkout details (products, amount, total, etc) directly from the client to the Dwolla server. Read about submitting directly »
Create a checkout session by communicating the checkout information from your server to the Dwolla server directly. Read about server-to-server »
Once the checkout is completed, the experience becomes identical on both workflows. The user is redirected back to the merchant site, along with some querystring parameters regarding the checkout, while Dwolla communicates the same information to the specified "callback" server. Read about checkout workflow »
Update: Dwolla's offsite gateway now supports guest checkouts. This gives non-Dwolla users the ability to fully checkout on a merchant's site without having a pre-existing Dwolla account. Read more about guest checkout.
User Experience
Here's a typical flow of the offsite gateway:
-
Customer initiates a checkout on the merchant site.
-
Customer is taken to Dwolla to complete the checkout.
-
Customer gets redirected back to the merchant's website.
Submit Directly
Generating the Form
Submit a form directly to the Off-Site Gateway with all information.
https://www.dwolla.com/payment/pay
Request Parameters:
| Param | Description | |
|---|---|---|
| * | key | Consumer key for the application. |
| * | signature | HMAC-SHA1 hexadecimal hash of the consumer key, timestamp, and order ID. |
| * | timestamp | Timestamp of the generated purchase order. Represented as the UNIX epoch time. |
| callback | URL to POST the transaction response to after the user authorizes the purchase. If not provided, will default to the Payment Callback URL set for the consumer application. If no default found, results in error. | |
| redirect | URL to return the user to after they authorize or cancel the purchase. If not provided, will default to the Payment Redirect URL set for the consumer application. If no default found, results in error. | |
| allowFundingSources | Flag to allow guest checkout and bank-funded payments. | |
| orderId | Merchant's order ID to identify the transaction. | |
| test | Flag if purchase order is for testing purposes only. Does not affect account balances and no emails are sent. The transaction ID will always be 1 in the responses. | |
| * | destinationId | Dwolla ID of the Dwolla account receiving the funds. Format will always match "812-xxx-xxxx". |
| * | amount | Amount of the order. Must be $0.01 or more. |
| * | shipping | Shipping total for the order. Must be $0.00 or more. |
| * | tax | Tax applied the order. Must be $0.00 or more. |
| facilitatorAmount | Amount of the facilitator fee to override. Only applicable if the facilitator fee feature is enabled. If set to 0, facilitator fee is disabled for transaction. Cannot exceed 25% of the total. | |
| notes | Note to attach to the transaction. Limited to 250 characters. | |
| * | name | Name of the item. Must be 100 characters or less. |
| * | description | Description of the item. Must be 200 characters or less. |
Generating the Signature
HMAC-SHA1 hexadecimal hash generated from the ampersand delimited text of the consumer key, timestamp, and orderId, even if an order ID is not provided. The text is then hashed using the consumer secret for the application as the hash key. The signature must be unique per request, so the timestamp will need to be generated with each request.
HMAC-SHA1 generation example with PHP:
<?php
$key = "YOUR_APPLICATION_KEY";
$secret = "YOUR_APPLICATION_SECRET";
$timestamp = time();
$order_id = 1;
$signature = hash_hmac('sha1', "{$key}&{$timestamp}&{$order_id}", $secret);
?>
Request example:
<form accept-charset="UTF-8" action="https://www.dwolla.com/payment/pay" method="post"> <input id="key" name="key" type="hidden" value="abcdefg" /> <input id="signature" name="signature" type="hidden" value="abcd" /> <input id="callback" name="callback" type="hidden" value="http://www.mywebsite.com/callback.aspx" /> <input id="redirect" name="redirect" type="hidden" value="http://www.mywebsite.com/redirect.aspx" /> <input id="test" name="test" type="hidden" value="true" /> <input id="name" name="name" type="hidden" value="Purchase" /> <input id="description" name="description" type="hidden" value="Description" /> <input id="destinationid" name="destinationid" type="hidden" value="812-111-1111" /> <input id="amount" name="amount" type="hidden" value="1.00" /> <input id="shipping" name="shipping" type="hidden" value="0.00" /> <input id="tax" name="tax" type="hidden" value="0.00" /> <input id="orderid" name="orderid" type="hidden" value="188375" /> <input id="timestamp" name="timestamp" type="hidden" value="1323302400" /> <button type="submit">Submit Order</button> </form>
Error example:
http://www.myurl.com/redirect?error=failure&error_description=Invalid+callback+URL
Error Codes
| Literal String | Explanation |
|---|---|
| "Invalid timestamp." | |
| "Payment has already been generated for application, timestamp, and order ID." | |
| "Invalid application signature." | |
| "Invalid amount." | |
| "Invalid shipping value." | |
| "Invalid tax value." | |
| "Invalid facilitator amount." | |
| "Invalid test value." |
Server-to-Server Checkout Request
Generating a Checkout Session
Submit a form directly to the Off-Site Gateway with all information.
The merchant website must POST a request to the Off-Site Gateway API providing a JSON formatted purchase order placed in the body of the request.
https://www.dwolla.com/payment/request
Request Parameters:
| Param | Description | |
|---|---|---|
| * | key | Consumer key for the application. |
| * | secret | Consumer secret for the application. |
| callback | URL to POST the transaction response to after the user authorizes the purchase. If not provided, will default to the Payment Callback URL set for the consumer application. If no default found, results in error. | |
| redirect | URL to return the user to after they authorize or cancel the purchase. If not provided, will default to the Payment Redirect URL set for the consumer application. If no default found, results in error. | |
| allowFundingSources | Flag to allow guest checkout and bank-funded payments. Read more about guest checkout. | |
| orderId | Merchant's order ID to identify the transaction. | |
| test | Flag if purchase order is for testing purposes only. Does not affect account balances and no emails are sent. The transaction ID will always be 1 in the responses. | |
| * | purchaseOrder | JSON object representing the purchase order instance. |
| customerInfo | JSON object representing the customer's proposed contact information. This information will be pre-filled and editable if Guest Checkout ("AllowFundingSources") is enabled. | |
| firstName | Customer's first name. | |
| lastName | Customer's last name. | |
| Customer's email address. | ||
| city | Customer's city. | |
| state | Customer's state. Must be specified in 2-characters abbr (e.g. CA for California). | |
| zip | Customer's zip code. | |
| * | destinationId | Dwolla ID of the Dwolla account receiving the funds. Format will always match "812-xxx-xxxx". |
| discount | Discount applied to the order. Must be $0.00 or a negative value but absolutely no more than the total value of the order. | |
| * | shipping | Shipping total for the order. Must be $0.00 or more. |
| * | tax | Tax applied the order. Must be $0.00 or more. |
| * | total | Total of the order. Must be $1.00 or more. Calculated as (item price x quantity) + tax + shipping + discount. |
| facilitatorAmount | Amount of the facilitator fee to override. Only applicable if the facilitator fee feature is enabled. If set to 0, facilitator fee is disabled for transaction. Cannot exceed 25% of the total. | |
| notes | Note to attach to the transaction. Limited to 250 characters. | |
| * | orderItems | Array of line items for the purchase order. |
| * | Name | Name of the item. Must be 100 characters or less. |
| * | Description | Description of the item. Must be 200 characters or less. |
| * | Price | Price of the item. Must be $0 or more. |
| * | Quantity | Quantity of the item. Must be 1 or more. |
Request example:
{
"Key": "testkey",
"Secret": "testsecret",
"AllowFundingSources": "true",
"Redirect": "http://example.com/redirect",
"Callback": "http://example.com/callback",
"PurchaseOrder": {
"customerInfo": {
"firstName": "Michael",
"lastName": "Schonfeld",
"email": "michael@dwolla.com",
"city": "New York",
"state": "NY",
"zip": "10006"
},
"DestinationId": "812-546-3855",
"OrderItems": [
{
"Description": "Description #1",
"Name": "Item #1",
"Price": 2.25,
"Quantity": 6
},
{
"Description": "Description #2",
"Name": "Item #2",
"Price": 3.04,
"Quantity": 10
},
{
"Description": "Description #3",
"Name": "Item #3",
"Price": 19.42,
"Quantity": 3
},
{
"Description": "Description #4",
"Name": "Item #4",
"Price": 29.95,
"Quantity": 1
}
],
"Discount": -9.95,
"Shipping": 5.95,
"Tax": 3.67,
"Total": 131.78,
"Notes": "A note on this order"
}
}
Successful response example:
{
"Result": "Success",
"CheckoutId": "C3D4DC4F-5074-44CA-8639-B679D0A70803"
}
Error response example:
{
"Result": "Failure",
"Message": "Invalid total."
}
Checking Out
The merchant's website will now need to redirect the user to the checkout endpoint providing the unique checkout ID within 5 minutes. After 5 minutes, the checkout ID will expire and a new request will need to be generated.
https://www.dwolla.com/payment/checkout/{CheckoutId}
Error Codes
| Literal String | Explanation |
|---|---|
| "Shipping rate cannot be less than $0." | |
| "Tax cannot be less than $0." | |
| "Discount cannot be greater than $0." | |
| "Total cannot be less than $.01." | |
| "Invalid total." | |
| "Notes length is too long. Maximum of 250 character is allowed." | |
| "Price on all order items cannot be less than $0." | |
| "Quantity on all order items cannot be less than 1." | |
| "Order item name length must be between 1 and 100 characters." | |
| "Order item description length must not exceed 200 characters." |
Literal strings returned in the API do no include the quotation marks used above.
Checkout Workflow
1. User Login
(a) If the user is not logged into the Dwolla network, they will be prompted to log in providing their email and password. Once the user successfully authenticates, they will be redirected to the checkout endpoint and the purchase order provided during the request will be presented to the user.
(b) If the user clicks the 'Cancel' button rather than logging in, they're redirected to the redirect URL with an error. If no redirect parameter was provided & no default Payment Redirect URL was set for the consumer application, they're redirected to dwolla.com.
https://www.myurl.com/redirect?error=failure&error_description=User Cancelled
2. Checkout
The user then will need to provide their PIN number and select the 'Place Order' button to complete the payment. If there are any errors, the payment will cancel and no funds will be transferred.
3. Server Callback
If the callback URL was provided during the request, the transaction details will be posted to the URL.
Callback POST parameters:
| Param | Description |
|---|---|
| Amount | Total amount of the purchase formatted to two decimal places. |
| CheckoutId | Unique purchase order ID generated by Off-Site Gateway. |
| ClearingDate | Date and time in which the funds are to be cleared into the destination account. |
| Error | Any errors that occurred during the checkout. |
| OrderId | Order ID provided during the request. |
| Signature | HMAC-SHA1 hexadecimal hash of the checkoutId and amount ampersand separated using the consumer secret of the application as the hash key. |
| Status | Status of the checkout session. Possible values: Completed, and Failed. |
| TestMode | Flagged to 'true' if purchase order is for testing purposes only, absent otherwise. |
| TransactionId | Dwolla transaction ID to identify the successful transaction. If in test mode, the transaction ID has a value of 'null'. |
{
"Amount": 0.01,
"CheckoutId": "f32b1e55-9612-4b6d-90f9-1c1519e588da",
"ClearingDate": "8/28/2012 3:17:18 PM",
"Error": null,
"OrderId": null,
"Signature": "c50e4b2a4c50ad795c3ee31370aec1a563565aa4",
"Status": "Completed",
"TestMode": "false",
"TransactionId": 1312616
}
4. User Redirect
Upon successful payment, the user will then be redirected back to the merchant's website provided by the redirect URL provided during the request.
Redirect URL parameters:
| Param | Description |
|---|---|
| checkoutId | Unique purchase order ID generated by Off-Site Gateway. |
| orderId | Order ID provided during the request. |
| test | Flagged to 'true' if purchase order is for testing purposes only, absent otherwise. |
| transaction | Dwolla transaction ID to identify the successful transaction. If in test mode, the transaction ID has a value of 'null'. |
| postback | If the callback URL provided and the postback response completed succesfully, a value of "success" - else, "failure". |
| amount | Total amount of the purchase formatted to two decimal places. |
| signature | HMAC-SHA1 hexadecimal hash of the checkoutId and amount ampersand separated using the consumer secret of the application as the hash key. |
| clearingDate | Date and time in which the funds are to be cleared into the destination account. |
| status | Status of the checkout session. Possible values: Completed, and Failed. |
Redirect URL format
https://www.myurl.com/redirect?signature={}&orderId={}&amount={}&checkoutId={}&status={}&clearingDate={}&transaction={}&postback={}
Redirect URL example
https://www.myurl.com/redirect?signature=c50e4b2a4c50ad795c3ee31370aec1a563565aa4&orderId=&amount=0.01&checkoutId=f32b1e55-9612-4b6d-90f9-1c1519e588da&status=Completed&clearingDate=8/28/2012%203:17:18%20PM&transaction=1312616&postback=success
* Dwolla's Signature
In both the user redirect querystring, and the callback POST request, Dwolla includes a signature that can be used to authenticate Dwolla's server identity. The signature is an HMAC-SHA1 hexadecimal hash of the checkoutId and amount ampersand separated using the consumer secret of the application as the hash key. Here's an example of a signature validation in some common languages:
Python
def verify_gateway_signature(proposed_signature, checkout_id, amount): import hmac import hashlib raw = '%s&%s' % (checkout_id, amount) signature = hmac.new(client_secret, raw, hashlib.sha1).hexdigest() return True if (signature == proposed_signature) else False
PHP
function verifyGatewaySignature($proposedSignature, $checkoutId, $amount) {
$amount = number_format($amount, 2);
$signature = hash_hmac("sha1", "{$checkoutId}&{$amount}", $apiSecret);
return $signature == $proposedSignature;
}