Mark orders as shipped
Overview
If you offer tangible products through 2Checkout, or if your cart utilizes your 2Checkout account’s shipping methods (such as WooCommerce), you are required to mark these orders as shipped to qualify associated funds for payout(s). Any tangible sale that is not marked as shipped will not deposit and therefore will not be paid out.
Note: Sales for intangible products and orders passed in without a shipping method using one of our third party cart parameter sets will deposit automatically and cannot be marked as shipped.
Mark orders as shipped
You can confirm the delivery of your orders from Orders and customers > Fulfillment confirmations, or directly from the order page.
In the Fulfillment confirmations page, select the order you want to confirm the delivery for, and click Confirm fulfillment. In case you select a single order, for which a shipping method was used during the ordering process, you will be asked to fill in a Tracking number and Additional shipping information (both optional fields).
You can select multiple orders and confirm the delivery simultaneously for them.
Note: Once an order has been marked as shipped, 2Checkout sends a notification to the customer, letting them know that the delivery process has started.
Print package slip
Use the Print Package Slip functionality to print a document that confirms the shipment date, together with the order information. Add this document to the package that will be shipped to your customers, so that they have an easy time matching the package with their order.
Edit shipping information
Update the shipping information for orders marked as shipped. In case you want to provide an additional note to your customers regarding the delivery, or want to fill in a different Tracking Number, use the Edit Shipping Information functionality from the order page. 2Checkout sends a notification to your customers once you update the shipping information from an order.
Unassign a PricingOption Group
Overview
Use the unassignPricingConfigurationOptionGroup method to remove a PricingOption Group from a PricingConfiguration.
Parameters
|
sessionID |
Required (string) |
|
|
Session identifier, the output of the Login method. Include sessionID into all your requests. Avangate throws an exception if the values are incorrect. The sessionID expires in 10 minutes. |
|
PricingConfigurationCode |
Required (string) |
|
|
Unique, system-generated pricing configuration identifier. |
|
PriceOptionsGroupAssigned |
Required (Object) |
|
|
Details below. |
|
PriceOptionsGroupAssigned |
Object |
|
Code |
Required (string) |
|
|
PricingOption Group identifier. |
|
Required |
Required (Object) |
|
|
True or false depending on whether the pricing options group is required during the purchase process or not. |
Response
bool(true)
Request
<?php
$host = "https://api.avangate.com";
$client = new SoapClient($host . "/soap/4.0/?wsdl", array(
'location' => $host . "/soap/4.0/",
"stream_context" => stream_context_create(array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false
)
))
));
function hmac($key, $data)
{
$b = 64; // byte length for md5
if (strlen($key) > $b) {
$key = pack("H*", md5($key));
}
$key = str_pad($key, $b, chr(0x00));
$ipad = str_pad('', $b, chr(0x36));
$opad = str_pad('', $b, chr(0x5c));
$k_ipad = $key ^ $ipad;
$k_opad = $key ^ $opad;
return md5($k_opad . pack("H*", md5($k_ipad . $data)));
}
$merchantCode = "YOURCODE123"; //your account's merchant code available in the 'System settings' area of the cPanel: https://secure.avangate.com/cpanel/account_settings.php
$key = "SECRET_KEY"; //your account's secret key available in the 'System settings' area of the cPanel: https://secure.avangate.com/cpanel/account_settings.php
$now = gmdate('Y-m-d H:i:s'); //date_default_timezone_set('UTC')
$string = strlen($merchantCode) . $merchantCode . strlen($now) . $now;
$hash = hmac($key, $string);
try {
$sessionID = $client->login($merchantCode, $now, $hash);
}
catch (SoapFault $e) {
echo "Authentication: " . $e->getMessage();
exit;
}
$PricingConfigurationCode = '54DCBC3DC8';
$PriceOptionsGroupAssigned = new stdClass();
$PriceOptionsGroupAssigned->Code = 'STORAGE';
$PriceOptionsGroupAssigned->Required = false;
try {
$UnassignedOption = $client-> unassignPricingConfigurationOptionGroup ($sessionID, $PricingConfigurationCode, $PriceOptionsGroupAssigned);
}
catch (SoapFault $e) {
echo "Options: " . $e->getMessage();
exit;
}
var_dump("Options", $UnassignedOption);
?>
Convert a trial
Overview
Use the convertTrial method to convert a trial to a paid subscription. In the eventuality of a conversion failure, you can use convertTrial again for the same trial subscription only after you let 24 hours pass since the initial attempt. The Avangate system attempts to automatically convert trials before they expire to full subscriptions, unless you made an attempt that failed less than 24 hours before the scheduled expiration deadline.
In case the trial conversion fails due to a transaction issue, the Avangate system sends unfinished payment follow-up emails to customers, provided that you set up lead management for your account.
Parameters
|
Parameters |
Type/Description |
|
sessionID |
Required (string) |
|
|
Session identifier, the output of the Login method. Include sessionID into all your requests. Avangate throws an exception if the values are incorrect. The sessionID expires in 10 minutes. |
|
SubscriptionReference |
Required (string) |
|
Unique, system-generated subscription identifier of the trial that you convert to a paid subscription. The unique identifier from the Avangate system:
Note: This method does not work for cancelled and/or expired trial subscriptions.
Avangate charges customers using the payment data attached to the trial subscription. In the case of credit/debit cards, if customers update their payment information in myAccount or if you update these details on behalf of your subscribers, the Avangate system uses the latest card info provided to charge subscription renewals. |
|
|
ExtendSubscriptionFromPaymentDate |
Optional (boolean) |
|
|
true = Set the moment of the conversion as the start date of the full subscription. Example: A 7 day trial purchased on October 29 for a monthly subscription converted on October 30 with $ExtendSubscriptionFromPaymentDate = true; features the following Billing cycle expiration: Nov 30, 2013 and Avangate scraps the initial trial expiration date November 5.
false = Set initial trial expiration deadline as the fhe full subscription start date. Example: A 10 day trial purchased on October 29 for a monthly subscription converted on October 30 with $ExtendSubscriptionFromPaymentDate = false; features the following Billing cycle expiration: December 9, with the first month period of the subscription added to the trial lifetime stretching until November 8.
Can be NULL. If not sent, the default value is false. |
Response
|
Boolean |
true or false depending on whether the changes were successful or not. |
Request
<?php
function callRPC($Request, $hostUrl, $Debug = true) {
$curl = curl_init($hostUrl);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSLVERSION, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Accept: application/json'));
$RequestString = json_encode($Request);
curl_setopt($curl, CURLOPT_POSTFIELDS, $RequestString);
if ($Debug) {
$RequestString;
}
$ResponseString = curl_exec($curl);
if ($Debug) {
$ResponseString;
}
if (!empty($ResponseString)) {
$Response = json_decode($ResponseString);
if (isset($Response->result)) {
return $Response->result;
}
if (!is_null($Response->error)) {
var_dump($Request->method, $Response->error);
}
} else {
return null;
}
}
$host = 'https://api.avangate.com/rpc/3.0/';
$merchantCode = "YOUR_MERCHANT_CODE";// your account's merchant code available in the 'System settings' area of the cPanel: https://secure.avangate.com/cpanel/account_settings.php
$key = "YOUR_SECRET_KEY";// your account's secret key available in the 'System settings' area of the cPanel: https://secure.avangate.com/cpanel/account_settings.php
$string = strlen($merchantCode) . $merchantCode . strlen(gmdate('Y-m-d H:i:s')) . gmdate('Y-m-d H:i:s');
$hash = hash_hmac('md5', $string, $key);
$i = 1; // counter for api calls
// call login
$jsonRpcRequest = new stdClass();
$jsonRpcRequest->jsonrpc = '2.0';
$jsonRpcRequest->method = 'login';
$jsonRpcRequest->params = array($merchantCode, gmdate('Y-m-d H:i:s'), $hash);
$jsonRpcRequest->id = $i++;
$sessionID = callRPC($jsonRpcRequest, $host);
var_dump($sessionID);
$SubscriptionReference = 'BF44555C6C';
$ExtendSubscriptionFromPaymentDate = true; //false can also be used if you want the subscription start date to be the moment when the trial was set to initially expire.
$jsonRpcRequest = array (
'method' => 'convertTrial',
'params' => array($sessionID, $SubscriptionReference, $ExtendSubscriptionFromPaymentDate),
'id' => $i++,
'jsonrpc' => '2.0');
var_dump (callRPC((Object)$jsonRpcRequest, $host, true));
Customer acquisition for digital goods and services companies
|
Long gone are the days in which the seller had the upper hand in the relationship with the buyer. The shift in power in the digital world calls for new digital acquisition strategies and solutions and strains the importance of "long-term" relationships. Customer acquisition is at the foundation of any digital strategy. Treating it with disregard can result in revenue leaks at every interaction point along the prospect-to-customer journey. If you add to this a poorly integrated combination of home-grown and legacy solutions, the lack of visibility and control will leave you far behind your competitors in today's digital commerce. Download this eBook on customer acquisition. You will get in-depth insights on:
Find out why customer acquisition is one critical part of the digital commerce lifecycle. |
![]() |
InLine Checkout with signature generation
Overview
Documentation for generating a JSON Web Token (JWT) used to authenticate yourself when using the 2Checkout Signature Generation API endpoint can be found here.
Generate a JWT for authenticating
It is strongly recommended you generate this using your own backend server and using a library to work with JSON Web Tokens (JWT).
In our examples below, you can see how we use the lcobucci/jwt PHP library in order to generate the JWT. You can use any library you want, but we strongly recommend using a library and not generating the JWT on your own.
JWT generation example
You can see a simple example of generating the token here:
<?php
namespace App\Helpers;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha512;
use Lcobucci\JWT\Signer\Key;
class JwtToken
{
static function getToken(){
$time = time();
$token = (new Builder())
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
->withClaim('sub', config('demo.vendor_code')) // Configures a new claim, called "sub" ( default subject of the JWT )
->getToken(new Sha512(), new Key(config('demo.vendor_secret'))); // Retrieves the generated token
return (string)$token;
}
}
This method returns a token, which in this case expires 60 minutes (3600 seconds) after it is generated. In most cases, this should allow you enough time, but you can modify this threshold as per your needs.
Using the library, call the new Lcobucci\JWT\Builder() and set the parameters specified in the How-to-generate-a-JWT documentation.
In order to use the JWT to authenticate in the 2Checkout Signature Generation API Endpoint, the algorithm used must be HS512, so you need to call the method getToken with the parameters new Sha512() for the algorithm and new Key('VENDOR_SECRET') for the JWT secret signature.
For new Key('VENDOR_SECRET') you need to use your Buy-link Secret Word, which can be found in your Merchant Control Panel under Integrations → Webhooks & API → Secret Word section.
Use cases
All the scenarios below use the /encrypt/generate/signature Convert Plus endpoint to generate a valid signature. This endpoint is further documented here and the format of the accepted parameters here.
Static cart with a signature generated before the page is rendered
This example showcases a simple HTML page that loads a pre-defined cart and its products. The signature is retrieved before the page is rendered and returned as a response to the client.

public function example1(){
$jwtToken = JwtToken::getToken();
$signature = CartSignature::getSignatureStaticCart($jwtToken);
return view('example-1')->with('signature', $signature);
}
The signature is generated as described here.
The body of the function getSignatureStaticCart looks like this:
public static function getSignatureStaticCart(string $jwtToken)
{
$curl = curl_init();
$products = config('examples.1.products');
$payload = new \stdClass;
$payload->merchant = config('demo.vendor_code');
$payload->lock = 1;
$payload->products = $products;
$payload->currency = 'USD';
curl_setopt_array($curl, [
CURLOPT_URL => https://secure.2checkout.com/checkout/api/encrypt/generate/signature,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'content-type: application/json',
'merchant-token: ' . $jwtToken,
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
throw new \Error('Curl error: ' . $err);
}
$signature = self::parse($response);
return $signature;
}
The relevant contents of the HTML page generated will be:
function buy() {
TwoCoInlineCart.cart.setCurrency('USD');
TwoCoInlineCart.products.removeAll();
TwoCoInlineCart.products.addMany(
{!! json_encode(config('examples.1.products')) !!}
);
TwoCoInlineCart.cart.setCartLockedFlag(true);
TwoCoInlineCart.cart.setSignature('{{$signature}}');
TwoCoInlineCart.cart.checkout();
}
The call to the TwoCoInlineCart checkout happens after setting the cart with exactly the same parameters used for the signature AND setting the signature (TwoCoInlineCart.cart.setSignature) to the one previously generated.
Dynamic cart with products selected by Customer - signature generated only once
This example showcases how you can have a dynamic cart, so the customer can select products and quantities. Because the products are added/changed by the customer inside the page, the signature must be generated when the shopping cart loads, using the products the customer selected; otherwise, the signature would be invalid and the cart would not load.
Clicking the buy-link would not immediately open the cart. Firstly, you must send the current cart payload to your backend and get the signature for the cart.
async function getSignature(){
const response = await fetch('/api/generate-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify({
cart: {
products: cart.products
}
})
});
return response.json();
}
The backend receives the products and makes the call to get the signature for this payload, then returns the signature.
public function generateSignature(Request $request){
$requestProducts = $request->input('cart.products');
$jwtToken = JwtToken::getToken();
$signature = CartSignature::getSignatureForProducts($jwtToken, $requestProducts);
return json_encode($signature);
} Then, you can set the signature returned by your backend and initialize the cart. This is the JavaScript snippet that does this.
async function buy() {
console.log('getting signature...');
let signature = await getSignature();
console.log('signature retrieved, ', signature);
TwoCoInlineCart.cart.setCurrency('USD');
TwoCoInlineCart.products.removeAll();
TwoCoInlineCart.products.addMany(
cart.products
);
TwoCoInlineCart.cart.setCartLockedFlag(true);
TwoCoInlineCart.cart.setSignature(signature);
TwoCoInlineCart.cart.checkout();
}Dynamic cart with products selected by customer - signature generated when the cart is updated

For the second use case above, the customer will have to wait for the generateSignature call when the buy button is pressed.
An alternative would be to generate and set a new signature every time the cart is updated. This way, more calls will be made to both your backend and the 2Checkout Signature Generation Endpoint, but the cart will load faster.
async function updateCart() {
computeCart();
await setSignature();
updateView();
}
While computeCart will update the payload, the setSignature call will make an Ajax to your backend, the same as in the second use case, and call TwoCoInlineCart.cart.setSignature(signature);.
async function setSignature() {
console.log('getting signature...');
let signature = await getSignature();
console.log('signature retrieved, ', signature);
TwoCoInlineCart.cart.setSignature(signature);
}
When clicking the Buy button, the cart will boot faster, as the signature call will already be called when the last cart modification is done.
You can choose between use cases 2 & 3, depending on your application's needs and objectives.
Cart with custom prices Products
If you have products with custom prices, you should always calculate the price of the product in your own backend. If you have frontend logic that generates and computes calculations in order to display the total, those prices should be used for only that, frontend display.
You must recompute the price on your backend and generate a signature with a payload that only you can validate: do not allow an AJAX call to your backend to calculate the total price.
public function signatureCustomPrices(Request $request){
$requestProducts = $request->input('cart.products');
// set custom prices
// !! do not trust prices coming on the request
// use the custom prices set in our system ( in this case they can be found in the @var $productsData )
$productsWithCustomPrices = [];
foreach($requestProducts as $key => $requestProduct){
$requestProductCustomPrice = $this->getCustomPriceByProductCode($requestProduct['code']);
$productsWithCustomPrices[$key]['code'] = $requestProduct['code'];
$productsWithCustomPrices[$key]['quantity'] = $requestProduct['quantity'];
$productsWithCustomPrices[$key]['custom-price'] = $this->formatCustomPrice($requestProductCustomPrice, $requestProduct['quantity']);
}
// create the payload with the product codes and quantities set by shopper, BUT the prices from the backend
$cartPayload = new \stdClass;
$cartPayload->merchant = config('demo.vendor_code');
$cartPayload->products = $productsWithCustomPrices;
$cartPayload->currency = 'USD';
// get JWT using token
$jwtToken = JwtToken::getToken();
// generate signature
$signature = CartSignature::getSignatureForTheEntirePayload($jwtToken, $cartPayload);
return json_encode($signature);
}
Depending on your own case, you can use the second or third scenarios to set the signature.
Cart with dynamic products
In order to generate a signature for dynamic products, the payload you send to the 2Checkout API Generation Endpoint should have the same parameters as the ones you use to set up the cart in your JavaScript code.
$cartPayload = new \stdClass;
$cartPayload->merchant = config('demo.vendor_code');
$cartPayload->currency = 'USD';
$cartPayload->dynamic = '1';
$cartPayload->products = config('examples.5.products');
$jwtToken = JwtToken::getToken();
$signature = CartSignature::getSignatureForTheEntirePayload($jwtToken, $cartPayload);
function buy() {
TwoCoInlineCart.setup.setMode('DYNAMIC');
TwoCoInlineCart.cart.setCurrency('USD');
TwoCoInlineCart.products.removeAll();
TwoCoInlineCart.products.addMany(
{!! json_encode(config('examples.5.products')) !!}
);
TwoCoInlineCart.cart.setSignature('{{$signature}}');
TwoCoInlineCart.cart.checkout();
}
Notice that you must set the payload currency and dynamic, the same way as you do in the JavaScript code.
After you compute this payload, you can just call the signature generation API endpoint.
public static function getSignatureForTheEntirePayload(string $jwtToken, \stdClass $cartPayload)
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => config('demo.signature_api_url'),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($cartPayload),
CURLOPT_HTTPHEADER => [
'content-type: application/json',
'merchant-token: ' . $jwtToken,
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
throw new \Error('Curl error: ' . $err);
}
$signature = self::parse($response);
return $signature;
}
Prefill customer data in order forms
Overview
Use 2Checkout's order form prefill feature to pre-populate forms in the ordering interface with customer data already stored in your shopping cart platform.
Scenarios
- You're using the secure.2checkout.com domain - Send the details to be auto-filled via a form using either GET or POST to https://secure.2checkout.com/order/pf.php.
- You're using a custom domain such as store.YourDomain.com - Use the custom domain when sending the details with either GET or POST to https://store.YourDomain.com/order/pf.php.
Workflow
2Checkout captures the sent parameters and redirects the customer to the link set by the "URL" parameter.
Parameters
|
Required |
|
|---|---|
|
URL |
The GET request created either in the Generate Sales Links area or dynamically. Use URL-encoding (RFC 1738) for the value of the URL parameter when working with custom-built links. |
|
MERCHANT |
Your 2Checkout Merchant Code (view) |
|
AUTOMODE |
(optional) Send this parameter with value = 1 to skip to the credit card details page, provided all billing information is sent as described below. If any of the fields below are incomplete, the regular form will be shown in order for the customer to fill in the missing fields. |
|
Optional: billing information |
|
|---|---|
|
BILL_FNAME |
Client first name |
|
BILL_LNAME |
Client last name |
|
BILL_COMPANY |
Company name for billing |
|
BILL_FISCALCODE |
Company Unique registration code(VAT ID) |
|
BILL_EMAIL |
E-mail address |
|
BILL_PHONE |
Phone number |
|
BILL_FAX |
Fax number |
|
BILL_ADDRESS |
Customer/Company physical address |
|
BILL_ADDRESS2 |
Customer/Company address (second line) |
|
BILL_ZIPCODE |
Customer/Company zip code |
|
BILL_CITY |
City |
|
BILL_STATE |
State/County |
|
BILL_COUNTRYCODE |
Country code (two letter code) |
|
Optional: delivery information |
|
|---|---|
|
DELIVERY_FNAME |
Client first name |
|
DELIVERY_LNAME |
Client last name |
|
DELIVERY_COMPANY |
Company name for delivery |
|
DELIVERY_PHONE |
Phone number |
|
DELIVERY_ADDRESS |
Client/company address (for delivery) |
|
DELIVERY_ADDRESS2 |
Client/company address (second line) |
|
DELIVERY_ZIPCODE |
Client/company zip code |
|
DELIVERY_CITY |
City |
|
DELIVERY_STATE |
State/County |
|
DELIVERY_COUNTRYCODE |
Country code (NL for Netherlands) |
URL encoded string
| Not encoded | Encoded |
|---|---|
|
|
Example: Submitting customer email in the buy-link
https://secure.2checkout.com/order/pf.php?MERCHANT=YourCode&BILL_EMAIL=john.doe@example.com&URL=https%3A%2F%2Fsecure.2checkout.com%2Forder%2Fcheckout.php%3FPRODS%3D123456%26QTY%3D1
Encrypt prefilled data
Send the data using either GET or POST.
Cryptographic Standard
Advanced Encryption Standard (AES), used with 256-bit keys, in GCM (Galois/Counter Mode), with a random and unique 'Initialization vector (IV)' 96-bit.
Encryption Key
Derive the encryption key from the secret key of your account from Integrations > Webhooks & API page, using a SHA256 HMAC and a cryptographically strong random nonce.
A cryptographic nonce is a random number that is used in communication protocols to assist maintain the privacy of communications. It is a single-use, generated using a cryptographically-strong, high-entropy, random-number generator.
Hash
Generate a keyed hash value using the HMAC method and the SHA2/ SHA3 hashing algorithm of the unencrypted URL (Buy Link) and your account's secret key.
GET request
Build your URL using this format:
https://secure.2checkout.com/order/pf.php?V=2&MERCHANT=<Your_Merchant_CODE>&DATA=<Nonce>.<Tag>.<IV>.<EncryptedData>&URL=<CHECKOUT_URL>All encrypted parameters are first Base64-encoded and then URL-encoded.
Encrypted Prefill Example PHP
<?php
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
echo '============================== BUY LINKS ==============================';
$baseUrl = "https://secure.2checkout.com";
$url = $baseUrl . '/order/checkout.php?CART=1&CARD=1';
$prods = '1234567';
$secretKey = '123&^^%$hjBJ';
$merchantCode = 'YourCode';
$urlString = 'PRODS=' . $prods . '&QTY=1&PRICES' . $prods . '[USD]=5&PLNKID='.strtoupper(generateRandomString());
$buyLink = "$baseUrl/order/checkout.php?PRODS=$prods&QTY=1&CART=1&CARD=2&SHORT_FORM=1";
$dataArray = [
'BILL_FNAME' => 'John',
'BILL_LNAME' => 'Doe',
'BILL_EMAIL' => 'test@test.com',
'BILL_COUNTRYCODE' => 'RO'
];
$data = http_build_query($dataArray) . '&URL=' . urlencode($buyLink);
dp('################################################################################################################');
dp('################################################### V2 #######################################################');
dp('################################################################################################################');
$cypherMethod = 'aes-256-gcm';
$ivLength = openssl_cipher_iv_length($cypherMethod);
$keyLength = 256/8;
$tagLength = 16;
$initialKeyingMaterial = $secretKey;
$keyDerivationNonceOrSalt = openssl_random_pseudo_bytes($keyLength);
$iv = openssl_random_pseudo_bytes($ivLength);
dp("NONCE (" . (strlen($keyDerivationNonceOrSalt)*8) . " bits): " . bin2hex($keyDerivationNonceOrSalt));
dp("IKM, IPN KEY (" . (strlen($initialKeyingMaterial)*8) . " bits, text): " . "'$initialKeyingMaterial'");
dp("IKM, IPN KEY (" . (strlen($initialKeyingMaterial)*8) . " bits, HEX): " . bin2hex($initialKeyingMaterial));
dp(" ");
dp("REQUIRED IV LENGTH: " . ($ivLength * 8) . ' bits');
dp("IV (" . (strlen($iv)*8) . " bits): " . bin2hex($iv));
dp(" ");
$encryptionKey = hash_hmac('sha256', $secretKey, $keyDerivationNonceOrSalt, true);
dp("DERIVED ENCRYPTION KEY (" . (strlen($encryptionKey)*8) . " bits): " . bin2hex($encryptionKey));
$encryptedData = openssl_encrypt($data, $cypherMethod, $encryptionKey, OPENSSL_RAW_DATA, $iv, $tag, $keyDerivationNonceOrSalt, $tagLength);
$decryptedString = openssl_decrypt($encryptedData, $cypherMethod, $encryptionKey, OPENSSL_RAW_DATA, $iv, $tag, $keyDerivationNonceOrSalt);
dp(" ");
dp("INPUT DATA (PLAIN): " . $data);
dp("SPECIFIED AUTHENTICATION TAG LENGTH: " . $tagLength . ' bytes');
dp("RESULTING AUTHENTICATION TAG: " . bin2hex($tag));
dp(" ");
dp("openssl_decrypt('" . bin2hex($encryptedData) . "', '" . bin2hex($cypherMethod) . "', '" . bin2hex($encryptionKey) . "', OPENSSL_RAW_DATA, '" . bin2hex($iv) . "', '" . bin2hex($tag) . "', '" . bin2hex($keyDerivationNonceOrSalt) . "')");
dp(" ");
dp("ENCRYPTED DATA: " . bin2hex($encryptedData));
dp("INPUT DATA (HEX): " . bin2hex($data));
dp("DECRYPTED DATA: " . bin2hex($decryptedString));
$base64Payload = base64_encode($keyDerivationNonceOrSalt)
. '.' . base64_encode($tag)
. '.' . base64_encode($iv)
. '.' . base64_encode($encryptedData);
dp(" ");
dp("DATA PAYLOAD STRUCTURE: " . 'base64_encode(NONCE).base64_encode(TAG).base64_encode(IV).base64_encode(ENCRYPTED DATA)');
dp("DATA PAYLOAD (BASE64): " . $base64Payload);
$checkoutUrl = 'checkout.php?PRODS=' . $prods . '&QTY=1';
$url = "$baseUrl/order/pf.php?V=2&MERCHANT={$merchantCode}&DATA=" . urlencode($base64Payload) . '&URL=' . urlencode($checkoutUrl);
dp($url);
function dp($s) {
if (empty($_SERVER['argv'])) {
echo "<pre>" . htmlentities($s) . "\n\n</pre>\n";
} else {
echo "$s\n\n";
}
}Encrypted Prefill Example C#
using System;
using System.Security.Cryptography;
using System.Text;
namespace Encrypted_Prefill_Example
{
internal class Example
{
public static void Main(string[] arg)
{
Console.WriteLine("============================== BUY LINKS ==============================");
string baseUrl = "https://secure.2checkout.com";
string prods = "12345678";
string secretKey = "YOUR_SECRET_KEY";
string merchantCode = "YOUR_MERCHANT_CODE";
string url = baseUrl + "/order/checkout.php?CART=1&CARD=1";
string urlString = "PRODS=" + Uri.EscapeDataString(prods) + "&QTY=1&PRICES" + Uri.EscapeDataString(prods) + "[USD]=5&PLNKID=" + GenerateRandomString().ToUpper();
string buyLink = baseUrl + "/order/checkout.php?PRODS=" + Uri.EscapeDataString(prods) + "&QTY=1&CART=1&CARD=2&SHORT_FORM=1";
string data = "BILL_FNAME=John&BILL_LNAME=Doe&BILL_EMAIL=test@test.com&BILL_COUNTRYCODE=RO&URL=" + Uri.EscapeDataString(buyLink);
Console.WriteLine("################################################################################################################");
Console.WriteLine("################################################### V2 #######################################################");
Console.WriteLine("################################################################################################################");
string cypherMethod = "AES256GCM"; // Adjusted for .NET's naming
int ivLength = 12; // GCM standard IV length
int keyLength = 32; // 256 bits
int tagLength = 16;
byte[] initialKeyingMaterial = Encoding.UTF8.GetBytes(secretKey);
byte[] keyDerivationNonceOrSalt = GenerateRandomBytes(keyLength);
byte[] iv = GenerateRandomBytes(ivLength);
PrintDebugInfo("NONCE", keyDerivationNonceOrSalt);
PrintDebugInfo("IKM, IPN KEY", initialKeyingMaterial);
PrintDebugInfo("IV", iv);
byte[] encryptionKey = new HMACSHA256(keyDerivationNonceOrSalt).ComputeHash(initialKeyingMaterial);
PrintDebugInfo("DERIVED ENCRYPTION KEY", encryptionKey);
byte[] encryptedData, decryptedData, tag;
encryptedData = Encrypt(Encoding.UTF8.GetBytes(data), encryptionKey, iv, out tag, keyDerivationNonceOrSalt);
decryptedData = Decrypt(encryptedData, encryptionKey, iv, tag, keyDerivationNonceOrSalt);
PrintDebugInfo("RESULTING AUTHENTICATION TAG", tag);
PrintDebugInfo("INPUT DATA", Encoding.UTF8.GetBytes(data));
PrintDebugInfo("ENCRYPTED DATA", encryptedData);
PrintDebugInfo("DECRYPTED DATA", decryptedData);
string base64Payload = Convert.ToBase64String(keyDerivationNonceOrSalt)
+ '.' + Convert.ToBase64String(tag)
+ '.' + Convert.ToBase64String(iv)
+ '.' + Convert.ToBase64String(encryptedData);
Console.WriteLine();
Console.WriteLine("DATA PAYLOAD STRUCTURE (BASE64-encoded components): NONCE.TAG.IV.ENCRYPTED_DATA");
Console.WriteLine("DATA PAYLOAD (BASE64): " + base64Payload);
string checkoutUrl = "checkout.php?PRODS=" + Uri.EscapeDataString(prods) + "&QTY=1";
url = baseUrl + "/order/pf.php?V=2&MERCHANT=" + Uri.EscapeDataString(merchantCode) + "&DATA=" + Uri.EscapeDataString(base64Payload) + "&URL=" + Uri.EscapeDataString(checkoutUrl);
Console.WriteLine();
Console.WriteLine(url);
}
private static void PrintDebugInfo(string prefix, byte[] data)
{
Console.WriteLine(prefix + " (" + (data.Length * 8) + " bits): " + BitConverter.ToString(data).Replace("-", ""));
Console.WriteLine();
}
private static string GenerateRandomString(int length = 10)
{
const string characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var randomString = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++)
{
randomString.Append(characters[random.Next(characters.Length)]);
}
return randomString.ToString();
}
private static byte[] GenerateRandomBytes(int length)
{
// Create a byte array to hold the random bytes
byte[] randomBytes = new byte[length];
// Generate random bytes using RNGCryptoServiceProvider
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(randomBytes);
}
return randomBytes;
}
private static byte[] Encrypt(byte[] plaintextBytes, byte[] key, byte[] iv, out byte[] tag, byte[] associatedData)
{
byte[] ciphertextBytes = new byte[plaintextBytes.Length];
tag = new byte[16]; // GCM tag length of 128 bits
using (AesGcm aesGcm = new AesGcm(key))
{
aesGcm.Encrypt(iv, plaintextBytes, ciphertextBytes, tag, associatedData);
}
return ciphertextBytes;
}
private static byte[] Decrypt(byte[] ciphertextBytes, byte[] key, byte[] iv, byte[] tag, byte[] associatedData)
{
byte[] plaintextBytes = new byte[ciphertextBytes.Length];
using (AesGcm aesGcm = new AesGcm(key))
{
aesGcm.Decrypt(iv, ciphertextBytes, tag, plaintextBytes, associatedData);
}
return plaintextBytes;
}
}
}
Manage test subscriptions
Overview
Import test subscription and data from your system or from a third-party and test advanced renewal and upgrade scenarios in the 2Checkout platform.
Availability
Import of test subscriptions is available for all accounts. Contact 2Checkout for additional details.
Requirements
Test subscriptions can be imported only for eStore orders.
For subscriptions that need to have payment information attached, only dummy credit card data can be assigned. Check here the list of credit cards that can be used for import.
Import test subscriptions via CSV
Import your existing subscriptions in our database, by using a UTF-8 encoded CSV file ("Comma Separated Values"). A sample .csv file is provided here.
Without credit card information
Import subscriptions including product and customer information by using the instructions from this article.
For subscriptions that don't have payment information assigned, do not send any values in the CardNumber and CardExpirationDate columns.
WITH CREDIT CARD INFORMATION
Import subscriptions with credit card information assigned by using the instructions from this article.
Only 2Checkout test credit cards can be used in the import. Check here the list of credit cards that can be used for import.
Cardholder name is composed by using the First name and Last name provided in the import file, as follows: FirstName + " " + LastName.
Import test subscriptions via API
Import your existing subscriptions by using our API capabilities.
Without credit card information
| JSON-RPC 4.0 | REST 4.0 | SOAP 4.0 |
|---|---|---|
| Import test subscriptions without payment information | Import test subscriptions without payment information | Import test subscriptions without payment information |
WITH CREDIT CARD INFORMATION
| JSON-RPC 4.0 | REST 4.0 | SOAP 4.0 |
|---|---|---|
| Import test subscriptions with credit card information | Import test subscriptions with credit card information | Import test subscriptions with credit card information |
Test subscriptions without credit cards
Explore manual renewal and upgrade scenarios with your imported test subscriptions.
Limitations
Automatic renewals (both via Web, and via API) are possible only after our internal cron has executed a renewal for your subscription, and an order has been generated for the imported subscription. Alternatively, you can place a manual renewal for the subscription generated instead of waiting for the cron to execute.
Upgrades via API are possible after our internal cron has executed a renewal for your subscription, and an order has been generated for the imported subscription. Alternatively, you can place a manual renewal for the subscription generated instead of waiting for the cron to execute.
Renewal using the hosted cart
Perform manual renewals for imported test subscriptions from your Control Panel.
- Go to Orders & Customers > Subscriptions.
- Search for the imported test subscription using a license identifier (email, reference, name).
- Open the subscription details page, and click on Renew subscription.
- Choose the manual renewal type.
- Confirm the billing information, and place the renewal order.
Upgrade using the hosted cart
Place upgrade orders for imported test subscriptions from your Control Panel.
- Go to Orders & Customers > Subscriptions.
- Search for the imported test subscription using a license identifier (email, reference, name).
- Open the subscription details page, and click on Upgrade subscription.
- Choose the new payment information type.
- Confirm the new payment details and place the upgrade order.
Manual renewal VIA API
Renew a subscription via API by using the 2Checkout Subscription Reference. Collect your recurring revenue with one of the following payment methods:
- Credit cards
- PayPal
- WeChat Pay
- iDEAL
- Purchase Order
- Wire
| JSON-RPC 4.0 | REST 4.0 | SOAP 4.0 |
|---|---|---|
| Place a manual renewal order | Place a manual renewal order | Place a manual renewal order |
Update a cross-sell campaign
Overview
Use the updateCrossSellCampaign method to update a cross-sell campaign for your 2Checkout account.
Request parameters
| Parameters | Type | Required | Description |
|---|---|---|---|
|
CampaignCode |
String |
Required |
The campaign code that can be used when placing orders. |
|
MasterProducts |
Array of strings |
Required |
Array of product codes to apply to this campaign. |
|
DisplayType |
String |
Required |
The display type of the campaign; Can be cart, review, finish. |
|
DisplayInEmail |
Boolean |
Required |
Determines if the campaign will be displayed in payment receipt emails. |
|
Products |
Array of objects |
Required |
Array of objects containing the product codes pointing to the promoted products when tied to each master product, the discount value, and the discount type (can only be percent). |
|
ProductCode |
String |
Required |
Product code of the product to be recommended to the shopper. |
|
Discount |
Float |
Required |
Value of the discount. |
|
DiscountType |
String |
Required |
Can only be 'PERCENT'. |
|
Name |
String |
Required |
Name of the campaign. |
| CampaignStatus |
String |
Optional |
The status of the cross-sell campaign. |
|
StartDate |
String |
Optional |
The date when the cross-sell campaign starts, formatted as YYYY-MM-DD. |
|
EndDate |
String |
Optional |
The date when the cross-sell campaign ends, formatted as YYYY-MM-DD. |
Response parameters
| Parameters | Type | Required | Description |
|---|---|---|---|
|
CampaignCode |
String |
Required |
The campaign code that can be used when placing orders. |
|
MasterProducts |
Array of strings |
Required |
Array of product codes to apply to this campaign. |
|
DisplayType |
String |
Required |
The display type of the campaign; Can be cart, review, finish. |
|
DisplayInEmail |
Boolean |
Required |
Determines if the campaign will be displayed in payment receipt emails. Can be 'true' or 'false'. |
|
Products |
Array of objects |
Required |
Array of objects containing the product codes pointing to the promoted products when tied to each master product, the discount value, and the discount type (can only be percent). |
|
ProductCode |
String |
Required |
Product code of the product to be recommended to the shopper. |
|
Discount |
Float |
Required |
Value of the discount. |
|
DiscountType |
String |
Required |
Can only be 'PERCENT'. |
|
Name |
String |
Required |
Name of the campaign. |
| CampaignStatus |
String |
Optional |
The status of the cross-sell campaign. |
|
StartDate |
String |
Optional |
The date when the cross-sell campaign starts, formatted as YYYY-MM-DD. |
|
EndDate |
String |
Optional |
The date when the cross-sell campaign ends, formatted as YYYY-MM-DD. |
Request sample
<?php
require ('PATH_TO_AUTH');
$csCampaign = new stdClass();
$csCampaign->CampaignCode = '2Xrl85eSkemBv3G3ea+9fg==';
$csCampaign->MasterProducts = ['C8851A5BC9'];
$csCampaign->DisplayType = 'review';
$csCampaign->DisplayInEmail = true;
$csCampaign->Name = 'testingRpc';
$csCampaign->StartDate = '2019-10-21';
$csCampaign->EndDate = '2021-10-21';
$csCampaign->CampaignStatus = 'ACTIVE';
$csCampaign->Products = [];
$csCampaign->Products[0] = new stdClass();
$csCampaign->Products[0]->ProductCode = '512712FA53';
$csCampaign->Products[0]->Discount = 20;
$csCampaign->Products[0]->DiscountType = 'PERCENT';
$jsonRpcRequest = new stdClass();
$jsonRpcRequest->jsonrpc = '2.0';
$jsonRpcRequest->method = 'updateCrossSellCampaign';
$jsonRpcRequest->params = array($sessionID, $csCampaign);
$jsonRpcRequest->id = $i++;
$csCampaignResponse = callRPC($jsonRpcRequest, $host, true)
Retrieve an order
Overview
Use the getOrder method to retrieve details on a specific order placed with dynamic product information, or catalog products, using the unique, system-generated reference.
Parameters
| Parameters | Type/Description |
|---|---|
|
sessionID |
Required (string) |
|
|
Session identifier, the output of the Login method. Include sessionID into all your requests. Avangate throws an exception if the values are incorrect. The sessionID expires in 10 minutes. |
|
orderReference |
Required (string) |
|
|
Order reference number of an older order, which is already approved/paid. |
Response
| Parameters | Type/Description |
|---|---|
|
Object (orders with catalog products) |
|
| Order information | Object (orders with dynamic product information) |
Request
<?php
require ('PATH_TO_AUTH');
$orderReference = 'YOUR_ORDER_REFERENCE';
try {
$fullOrderDetails = $client->getOrder ($sessionID, $orderReference);
}
catch (SoapFault $e) {
echo "fullOrderDetails: " . $e->getMessage();
exit;
}
var_dump("fullOrderDetails", $fullOrderDetails);
?>

