External Payment Processor: Using a custom payment gateway
support, ws.webtv, home, store, user, integration, sales, reports
For some clients, the built-in payment methods (Bank and PayPal) are not be enough and they might need to use a specific payment gateway to accept crtedit card payments in the WebTV. In order to allow this, we have implemented a simple way to accept payments through a payment processor script external to the WebTV.
How does it work?
1. You develop/implement the desired payment gateway (the payment processor script...).
2. You enable the "External Payment Processor" payment method in the Store extension configuration and supply the URL of the
payment processor script.
3. When your users click the "Pay order" button, they will be redirected to the payment processor URL (your script) and once you finish processing the payment, you redirect the users back to the WebTV in order to process the order.
How does the payment processor get the order details?
When the WebTV redirects the users to the payment processor, it will send the required data as GET vars (appended to the payment processor URL). The data will also include a validation signature (hash). More info below.
How does the WebTV knows what order got paid by the payment processor?
The payment processor script, when redirecting the user back to the WebTV, must also send the appropriate data as GET vars. The returned data must include the corresponding validation signature (hash). More info below.
Go to Store > Configuration (General tab), scroll down to "Sales" section and check the "Ext. Payment Processor" in "Allowed Payment Methods".
Then, save...
Go to the "Ext. Payment Processor" tab.
Provide the required information:
• Payment Processor URL: The URL of the payment processor script (must be an absolute URL).
• Key: Enter the key (string) that will be used to genertate the validation signature (hash). This key must also be used in your payment processor to generate the corresponding signatures.
• Enable Recurring Payments: (For subscriptions) enable this option if your payment processor script supports this functionality - Functionality available since WS.WebTV 1.7.5+.
Download a sample payment processor code in PHP:
Sample « without » recurring payments processing
Sample « with » recurring payments processing (updated to consider the case of subscriptions with trial period)
The WebTV will append the payment details to the payment processor script URL as GET vars. Example:
http://.../payment_processor.php?id_gateway={id_gateway}&id_order={id_order}&amount={amount}¤cy_code={currency_code}&order_number={order_number}&signature={signature}&id_user={user_id}
• id_gateway -> ID of the WebTV payment gateway
• id_order -> ID of the order to pay
IMPORTANT: You will need the id_gateway and id_order when returning to the WebTV.
• amount -> Amount to pay (decimal, for example: 10.5, 5, ...)
• currency_code -> Currency of the order (USD, EUR, etc.)
• signature -> A signature (hash) for validating the data. The sigtanure is generated with the key entered in the Store configuration, using HMAC SHA256.
Additional data:
• id_user -> ID of the User (buyer) in the WebTV
Validating the data
Before processing the payment, you should validate the data
using the signature. For doing this you must generate the signature, just like the WebTV does it, and compare it aginst the received one.
Example in PHP:
$key = "the secret key";
$data = array(
"id_gateway" => (string)$id_gateway,
"id_order" => (string)$id_order,
"amount" => (string)$amount,
"currency_code" => $currency_code,
"order_number" => (string)$order_number
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Once you have generated the signature, you must compare it against the one sent from the WebTV: If they match, you can continue.
When doing tests during development... if the signatures don't match (and you are sure the data you are receiving has not been altered), verify the keys used to generate the hashes (in the script and WebTV) are the same. If they are the same, verify the received data is being urldecoded and that you are generating the signature exactly as explained in the example above.
Once you have processed the payment, you must redirect the user back to the WebTV. In the return URL you must include the order details, payment result as well as a validation signature, as GET vars.
The data required by the WebTV (GET vars)
• iq -> ID of the order ("id_order").
• tp -> ID of the gateway ("id_gateway") with the prefix "gid_" and suffix "-step_2". Example: if the gateway ID is3, then tp would be gid_3-step_2
• status -> The payment status: "SUCCESS" or "ERROR"
• status_msg -> In case the status is ERROR then provide a message otherwise leave it blank.
• transaction -> ID of the transaction (this is normally the transaction ID provided by the payment gateway).
• signature -> A signature (hash) for validating the data. The sigtanure must be generated using the same key entered in the Store configuration, using HMAC SHA256.
Generating the validation signature (hash) for the return URL
You must generate a validation signature that the WebTV will use to determine if the data has not been altered in the return URL.
Example in PHP:
$key = "the secret key";
$data = array(
"id_gateway" => (string)$id_gateway,
"id_order" => (string)$id_order,
"status" => $status, // remember: "SUCCESS" or "ERROR"
"id_transaction" => (string)$id_transaction // the transaction ID...
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Building the return URL...
Once you have the required data and the validation signature, build the return URL.
Formula:
{webtv_base_url}/index.php?go=store&do=payOrder&iq={order id}&tp=gid_{WebTV gateway id}-step_2&status={status}&status_msg={urlencoded status message}&transaction={urlencoded transaction id}&signature={urlencoded signature}
Example:
Base URL of the WebTV: https://www.mywebtvdomain.com (use https:// if SSL is enabled in the WebTV)
ID of the order: 99
ID of the gateway: 3
Payment status: "SUCCESS"
Status message: "" (url encoded)
ID of the transaction: 98dfgdf89g7dg97df (url encoded)
Signature (hash): MGI5YzY0ZmE2N2Y4NW3ZDFmN2M0Zjc%3D (url encoded)
Generated return URL:
https://www.mywebtvdomain.com/index.php?go=store&do=payOrder&iq=99&tp=gid_3-step_2&status=SUCCESS&status_msg=&transaction=98dfgdf89g7dg97df&signature=MGI5YzY0ZmE2N2Y4NW3ZDFmN2M0Zjc%3D
If the webTV is able to validate the data then it will process the order, will grant the access to the purchased content and will display the corresponding message to the user.
The WebTV will append the payment details (one-time payment + recurring payments) to the payment processor script URL as GET vars. Example:
http://.../payment_processor.php?action=pay&id_gateway={id_gateway}&id_order={id_order}&amount={amount}¤cy_code={currency_code}&order_number={order_number}&signature={signature}&id_user={user_id}&rp_num={number_of_recurring_payments}&rp_1_...
One-time payment data
• id_gateway -> ID of the WebTV payment gateway
• id_order -> ID of the order to pay
IMPORTANT: You will need the id_gateway and id_order when returning to the WebTV.
• amount -> Amount to pay (decimal, for example: 10.5, 5, ...)
• currency_code -> Currency of the order (USD, EUR, etc.)
• signature -> A signature (hash) for validating the data. The sigtanure is generated with the key entered in the Store configuration, using HMAC SHA256.
Additional data:
• id_user -> ID of the User (buyer)
Validating the one-time payment data
Before processing the payment, you should validate the data
using the signature. For doing this you must generate the signature, just like the WebTV does it, and compare it aginst the received one.
Example in PHP:
$key = "the secret key";
$data = array(
"id_gateway" => (string)$id_gateway,
"id_order" => (string)$id_order,
"amount" => (string)$amount,
"currency_code" => $currency_code,
"order_number" => (string)$order_number
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Once you have generated the signature, you must compare it against the one sent from the WebTV: If they match, you can continue.
When doing tests during development... if the signatures don't match (and you are sure the data you are receiving has not been altered), verify the keys used to generate the hashes (in the script and WebTV) are the same. If they are the same, verify the received data is being urldecoded and that you are generating the signature exactly as explained in the example above.
Recurring payments data
In case the WebTV requests the setup of recurring payments then it will include additional GET vars
• rp_num -> Number of recurring payment requests.
Each recurring payment data fields will have the prefixe rp_n_{data_field_name}, where n is the number of the request. Please note that the number is required when returning to the WebTV.
• rp_n_sku -> SKU of the recurring payment request n - You can use this as a the description.
• rp_n_amount -> Amount of the recurring payment request n (currency is the same as the one time payment request).
• rp_n_period -> Period of the recurring payment request n (can be DAY, WEEK, MONTH or YEAR).
• rp_n_period_frequency -> Frequency for the period of the recurring payment request n (int value). "period" and "period_frequecy" determine the time interval for you to charge the buyer's credit card.
• rp_n_first_payment_date -> Date of the first payment of the recurring payment request n (Unix timestamp with UTC time zone). This is the date when you have to charge the buyer's credit card for the first time (for this recurring payment).
For example: if period is MONTH, period_frequency is 1 and first_payment_date is 22 Feb 2016, then
Payment 1 = 22 Feb 2016
Payment 2 = 22 Mar 2016
Payment 3 = 22 Apr 2016
• rp_n_signature -> A signature for validating the recurring payment request n. The sigtanure is generated with the key entered in the Store configuration, using HMAC SHA256.
Creating an array with the recurring payment requests and validating the data
While creating the array with the recurring payment requests, you will validate them by using their signatures. For doing this you must generate the signature, just like the WebTV does it, and compare it aginst the received one. If a signature does not match, you must add a failed request to the array, with an error message with the index "error"; otherwise, add the -valid- request to the array, for later processing.
Example in PHP:
$key = "the secret key"; // normally, defined at the beginning of the script
$recurring_payments = array();
if ( isset($_GET["rp_num"]) && $_GET["rp_num"]>0)
{
for ( $i=0; $i<$_GET["rp_num"]; $i++)
{
// Collect the recurring payment data "i"
$rp_sku = (isset($_GET["rp_".$i."_sku"]))? rawurldecode($_GET["rp_".$i."_sku"]) : "";
$rp_amount = (isset($_GET["rp_".$i."_amount"]))? $_GET["rp_".$i."_amount"] : 0.0;
$rp_period = (isset($_GET["rp_".$i."_period"]))? $_GET["rp_".$i."_period"] : "";
$rp_period_frequency = (isset($_GET["rp_".$i."_period_frequency"]))? $_GET["rp_".$i."_period_frequency"] : 0;
$rp_first_payment_date = (isset($_GET["rp_".$i."_first_payment_date"]))? $_GET["rp_".$i."_first_payment_date"] : 0;
$rp_signature = (isset($_GET["rp_".$i."_signature"]))? rawurldecode($_GET["rp_".$i."_signature"]) : "";
// Now, generate a signature to compare against the one in the "signature" variable.
$rp_computed_signature = base64_encode(hash_hmac('sha256', md5( $rp_sku . floatval($rp_amount) . $rp_period_frequency . $rp_period ) , $key, true));
if ( $rp_computed_signature!=$rp_signature)
{
// If signatures do not match, add a failed item (skip this from your processing but keep it as "failed" in order to return the result to the WebTV)
// Failed items must have an "error" array index, otherwise that array index must not be included in the return
$recurring_payments[$i]=array(
"error" => "Invalid recurring payment data or signature mismatch",
"sku" => "",
"amount" => 0,
"period" => "",
"period_frequency" => 0,
"first_payment_date" => 0,
"profile_id" => "",
"status" => "Perfil inválido"
);
}
else
{
// Add a valid item for later processing
$recurring_payments[$i]=array(
"sku" => $rp_sku,
"amount" => $rp_amount,
"period" => $rp_period,
"period_frequency" => $rp_period_frequency,
"first_payment_date" => $rp_first_payment_date,
// The following data fields must be populated after processing this request
"profile_id" => "MyUniqueInternalProfileID", // ID to identify the profile on future calls
"status" => "Active" // The status can be: Active, Pending, Cancelled, Suspended, Expired
);
}
}
}
Once the array has been generated, you must process the requests. As you can see on the example above, for each processed (valid) item you must return a unique ID, this is your internal ID for the recurring payment. It will be used by the WebTV to ask the payment processor script the status of a profile (to know if it is active, the date of the last/next payment, etc.) or to request the profile cancellation.
Once you have processed the one-time payment, as well as the recurring payments, you must redirect the user back to the WebTV. In the return URL you must include the order details, one-time and recurring payments results, as well as a the corresponding validation signatures, as GET vars.
One-time payment return data
The data required by the WebTV (GET vars)
• iq -> ID of the order ("id_order").
• tp -> ID of the gateway ("id_gateway") with the prefix "gid_" and suffix "-step_2". Example: if the gateway ID is3, then tp would be gid_3-step_2
• status -> The payment status: "SUCCESS" or "ERROR"
• status_msg -> In case the status is ERROR then provide a message otherwise leave it blank.
• transaction -> ID of the transaction (this is normally the transaction ID provided by the payment gateway).
• signature -> A signature (hash) for validating the data. The sigtanure must be generated using the same key entered in the Store configuration, using HMAC SHA256.
Generating the validation signature (hash) for the return URL
You must generate a validation signature that the WebTV will use to determine if the data has not been altered in the return URL.
Example in PHP:
$key = "the secret key";
$data = array(
"id_gateway" => (string)$id_gateway,
"id_order" => (string)$id_order,
"status" => $status, // remember: "SUCCESS" or "ERROR"
"id_transaction" => (string)$id_transaction // the transaction ID...
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Building the return URL...
Once you have the required data and the validation signature, build the return URL.
Formula:
{webtv_base_url}/index.php?go=store&do=payOrder&iq={order_id}&tp=gid_{WebTV_gateway_id}-step_2&status={status}&status_msg={urlencoded_status_message}&transaction={urlencoded_transaction_id}&signature={urlencoded_signature}
Example:
Base URL of the WebTV: https://www.mywebtvdomain.com (use https:// if SSL is enabled in the WebTV)
ID of the order: 99
ID of the gateway: 3
Payment status: "SUCCESS"
Status message: "" (url encoded)
ID of the transaction: 98dfgdf89g7dg97df (url encoded)
Signature (hash): MGI5YzY0ZmE2N2Y4NW3ZDFmN2M0Zjc%3D (url encoded)
Generated return URL:
https://www.mywebtvdomain.com/index.php?go=store&do=payOrder&iq=99&tp=gid_3-step_2&status=SUCCESS&status_msg=&transaction=98dfgdf89g7dg97df&signature=MGI5YzY0ZmE2N2Y4NW3ZDFmN2M0Zjc%3D
Recurring payments return data
In case there is any recurring payment request, you must also include the data of each request (as GET vars), as well as a flag to note the presence of the recurring payments.
- The recurring payments flag: To the aforementioned tp GET var you must ass the string "-rp_1". Example: &tp=gid_3-step_2-rp_1
- For each recurring payment request, the corresponding GET vars are necessary, in the form rp_n_{data_field_name}, where n is the number of the request:
• rp_n_error -> (optional) Only if the "error" index exists in the recurring payments array.
• rp_n_profile_id -> ...
• rp_n_status -> ...
• rp_n_first_payment_date -> ...
• rp_n_signature -> A signature (hash) for validating the data. The sigtanure must be generated using the same key entered in the Store configuration, using HMAC SHA256.
Example in PHP:
$key = "the secret key";
$return_url = "...."; // The return URL build so far
if ( !empty($recurring_payments))
{
// Add the recurring payments return flag
$return_url = str_replace('-step_2&','-step_2-rp_1&',$return_url);
foreach ( $recurring_payments as $rp_index => $rp_data)
{
// Generating the validation signature
$rp_signature = base64_encode(hash_hmac('sha256', md5( $rp_data["profile_id"] . $rp_data["status"] ) , $key, true));
// Append GET vars to the return URL
if ( isset($rp_data["error"])) $return_url .= "&rp_".$rp_index."_error=".urlencode($rp_data["error"]);
$return_url .= "&rp_".$rp_index."_profile_id=".urlencode($rp_data["profile_id"]);
$return_url .= "&rp_".$rp_index."_status=".urlencode($rp_data["status"]);
$return_url .= "&rp_".$rp_index."_first_payment_date=".$rp_data["first_payment_date"];
$return_url .= "&rp_".$rp_index."_signature=".urlencode($rp_signature);
}
}
Redirect the user back to the WebTV
If the webTV is able to validate the data then it will process the order, will grant the access to the purchased content, will assign the recurring payment profiles to the subscriptions and will display the corresponding message to the user.
The WebTV will append the request data to the payment processor script URL as GET vars. Example:
http://.../payment_processor.php?action=rp_status&profile_id={recurring_payment_profile_id}&signature={signature}
Request data (GET vars)
• profile_id -> ID of the recurring payment profile. This was the ID this processor returned to the WebTV when setting up a recurring payment profile.
• signature ->A signature (hash) for validating the data. The sigtanure must be generated using the same key entered in the Store configuration, using HMAC SHA256.
Validating the request data
Before continuing, you should validate the data
using the signature. For doing this you must generate the signature, just like the WebTV does it, and compare it aginst the received one.
Example in PHP:
$key = "the secret key";
$profile_id = (isset($_GET["profile_id"]))? $_GET["profile_id"] : "";
$data = array(
"action" => "rp_status",
"profile_id" => (string)$profile_id
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Once you have obtained the profile information you must return the data as a JSON formatted array.
Array indexes required by the WebTV
• error -> (optional) Only if there was a processing error.
• status -> Status of the profile. Possible values: Active, Pending, Cancelled, Suspended, Expired
• last_payment_date -> Date of the last (successful ) payment (Unix timestamp, with UTC time zone). IMPORTANT: This will be used by the WebTV in order to know if a recurring payment (cicle) is actually paid. Return 0 if no payment has been made.
• next_payment_date -> Date of the next payment (Unix timestamp, with UTC time zone).
Examples in PHP:
-Success-
echo json_encode( array(
"status" => "Active",
"last_payment_date" => 1422344201,
"next_payment_date" => 1425022599
));
-Error-
echo json_encode( array(
"error" => "A deatiled error message..."
));
The WebTV will append the request data to the payment processor script URL as GET vars. Example:
http://.../payment_processor.php?action=rp_cancel&profile_id={recurring_payment_profile_id}&signature={signature}
Request data (GET vars)
• profile_id -> ID of the recurring payment profile. This was the ID this processor returned to the WebTV when setting up a recurring payment profile.
• signature ->A signature (hash) for validating the data. The sigtanure must be generated using the same key entered in the Store configuration, using HMAC SHA256.
Validating the request data
Before continuing, you should validate the data
using the signature. For doing this you must generate the signature, just like the WebTV does it, and compare it aginst the received one.
Example in PHP:
$key = "the secret key";
$profile_id = (isset($_GET["profile_id"]))? $_GET["profile_id"] : "";
$data = array(
"action" => "rp_cancel",
"profile_id" => (string)$profile_id
);
$signature = base64_encode(hash_hmac('sha256', json_encode($data), $key, true));
Once you have cancelled the profile you must return the result as a JSON formatted array.
Array indexes required by the WebTV
• error -> (optional) Only if there was a processing error.
• status -> Status of the profile after cancellation. Must be Cancelled.
Examples in PHP:
-Success-
echo json_encode( array(
"status" => "Cancelled"
));
-Error-
echo json_encode( array(
"error" => "A deatiled error message..."
));