Merchant Details
Ordergroove will use a unique value to identify your merchant within our platform. This value is represented by its name in angle brackets as a placeholder throughout this page.
Name | Merchant ID |
Description | Ordergroove's identifier of the client in our system |
Value | <"PROVIDED BY ORDERGROOVE"> |
Placeholder | <MERCHANT_ID> |
Domains & URL's
Environment |
Staging |
Production |
Placeholder |
---|---|---|---|
Front End Script Tags |
<STATIC_DOMAIN> |
||
Subscription Creation |
<SC_DOMAIN> |
||
API Endpoints |
https://staging.v2.ordergroove.com/<"web_service">/<"function"> |
https://api.ordergroove.com/<"web_service">/<"function"> |
<API_DOMAIN> |
SFTP Drop Off |
sftp://<"username">@staging.feeds.ordergroove.com |
sftp://<"username">@feeds.ordergroove.com |
<SFTP_DROP_SITE> |
Character Encoding
Ordergroove only supports ASCII character encoding currently (not UTF-8). This means that UTF-8 special characters not supported by ASCII encoding should not be accepted in Subscription Creation Posts, API updates, or the Feeds.
Cache Busting
Calls to OrderGroove scripts should be made without cache busters. Please do not include cache busters in calls to OrderGroove, even those added by default when formulating script calls with jquery.
Subscription Manager
Please create a new page under your site's My Account section to hold the Subscription Manager. This URL should redirect to the login page if the user is not logged in. Please provide the relative path of this page to OrderGroove. The path should be the same in all environments.
Please add the following div to the page:
vs MSI
Please note that in the code examples the Subscription Manager is written as MSI which is our legacy name.
When writing and using code MSI is the correct method.
<div id="og-msi"ng-jq=""></div>
<script type="text/javascript"src="<STATIC_DOMAIN>/<MERCHANT_ID>/msi.js"></script>
Steps for OG to load the Subscription Manager:
- Browser loads Subscription Manager by visiting Subscription Manager URL.It must be HTTPS.
- Merchant's server returns the Subscription Manager page with OG javascript and If the user is logged in.
- OG javascript, on the Subscription Manager browser page, loads a hidden iframe to the merchant's auth page.Auth page must be HTTPS and on the same root domain as the Subscription Manager page.
- On page load, merchant creates a signature with the user_id and timestamp as specified below. Merchant sets the signature, the user_id and the token as a "secure" cookie, but not an "HTTP only" cookie.The path of the cookie should be "/".
- Subscription Manager browser page retrieves the signature from the secure cookie and uses it to make API calls.
- OG server returns the appropriate response if the signature matches and the timestamp is within the last 2 hours.
Authentication Page
This section details how to create an authentication page for Ordergroove. Users are authenticated for the secure display of the Subscription Manager and Impulse Upsell offers.
Requirements
- Must NOT require logging in to view
- Must be HTTPS
Path to Authentication Page
Please provide the relative path of this page to OrderGroove. The path should be the same in all environments.
og_auth Cookie & Signature Creation
When theog-auth
page loads, you should create a signature and set it as a "secure" cookie and not HTTP only. Please refer to the HMAC authentication instructions in the Security section below.
NOTE
- Only set the og_auth cookie if the user is logged in and set it with a 2 hours expiry. If the user is not logged in, the auth page should still load but no cookie should be set. Please delete this cookie whenever the user logs out to ensure that access to OG's information has been terminated when the user's session ends.
- Do not URL encode this cookie.
The cookie contents will have the following format:
<MERCHANT_USER_ID>|<SECONDS_SINCE_EPOCH>|<SIGNATURE>
NOTE
- The merchant_user_id should be the value you use to identify the customer in your system (e.g. TestCustomer123)
- Seconds since epoch should not include milliseconds. This will be a 10-digit number.
Contents Of Page
The page should only contain a single script file and should not load merchant page headers or any other information. The page will never be visible to the user.
<html>
<head>
<scripttype="text/javascript"src="<STATIC_DOMAIN>/<MERCHANT_ID>/auth.js"></script>
</head>
<body>
</body>
</html>
Security
HMAC Authentication
Item | Definition |
---|---|
RAW_DATA_POINT | One data point used as the input for the signature generation function. For the purpose of this integration, this will always be the user ID. |
SECONDS_SINCE_EPOCH | The Unix epoch (or Unix time, POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT). This will be a 10-digit number. |
SHA-256 | The hash function set as a parameter of the HMAC signature generation function. |
HASH_KEY | Secret hash key provided to you by your Ordergroove team, unique to your merchant. |
Using the HMAC hash function, generate a signature using SHA-256. The function input also includes the concatenation of RAW_DATA_POINT and SECONDS_SINCE_EPOCH (see definitions above) with a pipe character in between, and the secret hash key.
If you are sending the signature in an HTTP request, you must URL encode it before sending.
Example:
signature = hash_hmac("sha256", "<RAW_DATA_POINT>|<SECONDS_SINCE_EPOCH>", "<HASH_KEY>");
AES Encryption
Algorithm: AES
Mode: ECB
Padding: Right-padded with '{' characters
Block-size: 32 bytes
First, with the hash key, generate anAEScipher usingECBmode. ie: cipher = AES.new(hash_key)
To encode data:
- Pad the data you want to encode with the "{" character so that it is a multiple of 32 characters in length.
- Pseudo code example: (data + (32 - length(data) modulus 32) * "{" )
- If data="something" the result of padding data would be "something{{{{{{{{{{{{{{{{{{{{{{{"
- Encrypt the padded data with the cipher
- Pseudo code example: cipher.encrypt(padded_data)
To decode data:
- Decrypt with the cipher
- Pseudo code example: cipher.decrypt()
- Remove all the padding characters
- Pseudo code example: decrypted_data.rstrip("{"
PHP
<?php
class Example {
const CYPHER = MCRYPT_RIJNDAEL_128;
const MODE = MCRYPT_MODE_ECB;
const BLOCK_SIZE = 32;
const PADDING = '{';
protected $_hashKey = null;
public function setHashKey($data) {
$this->_hashKey = $data;
}
public function getHexHashKey() {
return pack('H*', bin2hex($this->_hashKey));
}
public function pad($data) {
$data . str_repeat(self::PADDING,(self::BLOCK_SIZE - (strlen($data) % self::BLOCK_SIZE)));
}
public function strip($data) {
return str_replace(self::PADDING, '', $data);
}
public function encrypt($data) {
return trim(base64_encode(mcrypt_encrypt(self::CYPHER, $this->getHexHashKey(), $data, self::MODE)));
}
public function decrypt($data)
{
return $this->strip(mcrypt_decrypt(self::CYPHER,$this->getHexHashKey(),base64_decode($data),self::MODE));
}
}
$obj = new Example;
$obj->setHashKey('Mt!ZQ45q&GHsgiRD8{NB-_h87#rjvbn0');
$ccNumber = '4111111111111111';
echo "Original credit card number: $ccNumber<BR>";
$encryptedCcNumber = $obj->encrypt($ccNumber);
echo "Encrypted credit card number: $encryptedCcNumber<BR>";
$unencryptedCcNumber = $obj->decrypt($encryptedCcNumber);
echo "Unencrypted credit card number: $unencryptedCcNumber";
Python
BLOCK_SIZE = 32
PADDING = '{'
def pad(s):
return s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
def EncodeAES(c, s):
return base64.b64encode(c.encrypt(pad(s)))
def DecodeAES(c, e):
return c.decrypt(base64.b64decode(e)).rstrip(PADDING)
hash_key = 'Mt!ZQ45q&GHsgiRD8{NB-_h87#rjvbn0'
cipher = AES.new(hash_key)
encoded = EncodeAES(cipher, "something")
something = DecodeAES(cipher, encoded)
Product Feed
The Product Feed is a file that is transmitted via SFTP to our platform on a recurring basis, to allow us to keep synchronized with your product information. This feed should contain all products, regardless of whether they are eligible for subscriptions or not. We check for the existence of these files every 30 minutes, so you can provide a new copy as often as once every 30 minutes.
If the product feed is not sent continuously throughout the day it is highly recommended that the product feed run prior to order placement run-time, in order to prevent any orders from being rejected for out of stock issues.
The product feed should be created with the following format:
Filename: <MERCHANT_ID>.Products.xml
Drop site location: <SFTP_DROP_SITE>
NOTE
Any applicable field containing special characters (e.g. &, trademark symbols, accents, etc.) must be wrapped in CDATA. Best practice is to always wrap product names and category names in CDATA at a minimum.
Field | Description | Data Type | Validation |
---|---|---|---|
name | The name of the product. | String, up to 1024 characters | |
product_id | The unique identifier for the product in your database. | String, up to 64 characters | |
sku | Product's SKU. | String, up to 64 characters | |
groups | Used for SKU swap. The name of the "parent" product name, and/or other product grouping. | String, up to 64 characters | |
price | The price of the product in US dollars and cents. | Decimal, up to 99999999.99 | Must be a decimal (not wrapped in quotation marks), must have two digits after decimal point. This should not be set to anything other than what should the customer should be charged (shouldn't change to $0, for example). |
details_url | Fully qualified URL of the product's detail page. | String, up to 400 characters | |
image_url | Fully qualified URL of the product's image file. NOTE: These URLs must be HTTPS. |
String, up to 400 characters | |
autoship_eligible | A flag that marks the product as eligible for subscriptions. 0=Not eligible 1=Eligible |
Tinyint(1) | Must be 1 or 0 as a value, must not be wrapped in quotation marks |
in_stock | A flag that marks the product as available in terms of inventory. 1=In stock, 0=Out of Stock. A product can become temporarily out of stock, which will temporarily stop orders being placed with that product. Additionally, no checkout flow offers will be served for out of stock products, but impulse upsell offers will be served as the product may come back in stock prior to order placement. | Tinyint(1) | Must be 1 or 0 as a value, must not be wrapped in quotation marks |
discontinued | An optional flag that marks the product as no longer available. 0=Not discontinued 1=Discontinued. When we receive a discontinued flag, all subscriptions with that product will be cancelled and the customer will be notified via email. If a product is discontinued, it must be ineligible for autoship (autoship_eligible = 0) out of stock (in_stock = 0). |
Tinyint(1) | Must be 1 or 0 as a value, must not be wrapped in quotation marks. NOTE: In order to discontinue a product it must also be marked as Not Eligible and Out of Stock. |
categories | Nested list of category IDs that apply to this product. | String, each category ID can be up to 64 characters | |
extra_data | The value to be displayed in the SKU swap dropdown. Optional field - can be used to display a name other than the full product name. | String, up to 64 characters | |
relationships | This is where the related product ID can be defined for discontinued product replacement SKU swap. The relationship name within this node will determine if an e-mail will be triggered. If the relationship name = discontinued_silent_replacement, subscribers will NOT be informed of the swap. If the relationship name = discontinued_replacement, an e-mail will be sent to all subscribers of the product to inform them of the swap. NOTE: The subscriber will only be notified once the swap has been completed. |
String, up to 64 characters | |
every | The number of days, weeks, or months used in order to specify a product-specific default frequency. | INT, up to 10 characters | |
every_period | The frequency period used in order to specify a product-specific default frequency where: 1 = days 2 = weeks 3 = months 4 = years |
SMALLINT, up to 6 characters | Example: <"every>4</every"> <"every_period">2<"/every_period">" The above example translates into 4 weeks |
XML
<products>
...
<product>
<name><![CDATA[Product XYZ]]></name>
<product_id>AB4483902</product_id>
<sku>45987234980</sku>
<groups>
<group type="sku_swap"><![CDATA[Product Group A]]></group>
<group type="incentive"><![CDATA[Incentive Group A]]></group>
</groups>
<price>19.99</price>
<details_url>http://www.merchanturl.com/details/product.com</details_url>
<image_url>https://www.merchanturl.com/images/product.jpg</image_url>
<autoship_eligible>1</autoship_eligible>
<in_stock>1</in_stock>
<discontinued>0</discontinued>
<categories>
<category><![CDATA[CATEGORY_ID_1]]></category>
<category><![CDATA[CATEGORY_ID_2]]></category>
<category><![CDATA[CATEGORY_ID_3]]></category>
</categories>
<extra_data>
<field key="variant_name">Size X</field>
</extra_data>
<relationships>
<relationship>
<name>discontinued_replacement</name>
<related>
<product_id>12</product_id>
</related>
</relationship>
</relationships>
<every>6</every>
<every_period>2</every_period>
</product>
...
</products>
Product Feed Feature Example
SKU Swap
SKU swap allows a user to change the product associated with their subscription. Once changed, all future orders for that subscription will be for the new product, but the subscription ID will remain intact. To enable this feature, you would add the following nodes to the product feed.
- Groups node with one group containing the "parent" product name.
- (Optional) Extra data field called variant_name containing value to be displayed in the Subscription Manager SKU swap dropdown.
- Only needed if you don't want to display the full product name in the dropdown.
For example, you could create a group so that all"<item_group_example>Max's Delectable Cat Treatz"are swappable for one another. Instead of displaying the whole product name, e.g."<item_variant_example>Max's Delectable Cat Treatz - Tuna Flavor",you could set the dropdown display text to be simply"<item_variant_condensed_name_example>Tuna Flavor"using the extra data field.
SKU Swap XML Example:
<products>
...
<product>
<name><![CDATA[Max's Delectable Cat Treatz - Tuna Flavor]]></name> // Modify this to make it specific to the client
<product_id><![CDATA[CT-4050]]></product_id>
<sku><![CDATA[50020401]]></sku>
<groups>
<group><![CDATA[Max's Delectable Cat Treatz]]></group> // Modify this to make it specific to the client
</groups>
...
<extra_data>
<field key="variant_name"><![CDATA[Tuna Flavor]]></field> // Modify this to make it specific to the client
</extra_data>
</product>
<product>
<name><![CDATA[Max's Delectable Cat Treatz - Chicken Flavor]]></name> // Modify this to make it specific to the client
<product_id><![CDATA[CT-4051]]></product_id>
<sku><![CDATA[50020402]]></sku>
<groups>
<group><![CDATA[Max's Delectable Cat Treatz]]></group> // Modify this to make it specific to the client
</groups>
...
<extra_data>
<field key="variant_name"><![CDATA[Chicken Flavor]]></field> // Modify this to make it specific to the client
</extra_data>
</product>
<product>
<name><![CDATA[Max's Delectable Cat Treatz - Bacon Flavor]]></name> // Modify this to make it specific to the client
<product_id><![CDATA[CT-4052]]></product_id>
<sku><![CDATA[50020403]]></sku>
<groups>
<group><![CDATA[Max's Delectable Cat Treatz]]></group> // Modify this to make it specific to the client
</groups>
...
<extra_data>
<field key="variant_name"><![CDATA[Bacon Flavor]]></field> // Modify this to make it specific to the client
</extra_data>
</product>
...
</products>
The above SKU Swap XML example would result in the three flavor variations of<item_group>"Max's Delectable Cat Treatz" being selectable options for any of the associated products. The associated products will display their "variant_name" value. For example, "Max's Delectable Cat Treatz - Bacon Flavor" will be displayed as<product_variant_name> "Bacon Flavor" in the SKU Swap dropdown.
Subscription Manager SKU Swap Dropdown Result:
Want To Switch To a Different Product?
- Bacon Flavor
- Chicken Flavor
- Tuna Flavor
Purchase Post
When any user completes checkout, Ordergroove needs to be notified about this event. You will send a secure HTTP POST that will provide Ordergroove with the necessary information to create one or more subscriptions on our servers. Ordergroove will be expecting a purchase POST regardless of whether or not the customer is checking out with a subscription item. If there is a subscription item, Ordergroove will create a new subscription and allow the customer to begin managing the subscription. If there is not a subscription item, Ordergroove will instead log the OG cart session as a non-subscription checkout and clear the cart session.
Destination
- Staging:https://staging.sc.ordergroove.com/subscription/create
- Production:https://sc.ordergroove.com/subscription/create
NOTES
-
All fields in the request are required for Ordergroove's subscription creation field validation unless noted as optional.
- Any requests made to the Ordergroove servers from your application should be accompanied with a 5 second timeout. In the event that Ordergroove is unavailable, your application should be able to continue to run seamlessly.
- Each string in the body of the POST must be URL encoded. Please do NOT URL encode the entire POST and do NOT URL encode the authorization headers
Header
headers = {
'authorization': '{"public_id": "<MERCHANT_PUBLIC_ID>", "ts": <SECONDS_SINCE_EPOCH>, "sig_field": "<MERCHANT_USER_ID>", "sig": "<SIGNATURE>"}',
'content-type': 'application/json'
}
Request
create_request={
"merchant_id": "<MERCHANT_ID>",
"session_id": "<SESSION_ID>",
"og_cart_tracking": false, // Use this for cartless subscription creation ONLY . NOTE - This field must be used instead of the "session_id" field. You CANNOT use "session_id" and "og_cart_tracking" in the same request.
"merchant_order_id": "abc123",
"user": {
"user_id": "7654321",
"first_name": "Jane",
"last_name": "Rouse",
"email": "jane.rouse@ordergroove.com",
"phone_number": "555-555-5555",
"shipping_address": {
"first_name": "Jane",
"last_name": "Rouse",
"company_name": "Ordergroove", // Optional
"address": "75 Broad Street",
"address2": "23rd Floor", // Optional
"city": "New York",
"state_province_code": "NY",
"zip_postal_code": "10004",
"phone": "555-555-5555",
"fax": "", // Optional
"country_code": "US" // Must be two characters maximum
},
"billing_address": { // Optional - OG can store billing address fields if needed
"first_name": "Jane",
"last_name": "Rouse",
"company_name": "Ordergroove", // Optional
"address": "75 Broad Street",
"address2": "23rd Floor", // Optional
"city": "New York",
"state_province_code": "NY",
"zip_postal_code": "10004",
"phone": "555-555-5555",
"fax": "", // Optional
"country_code": "US" // Must be two characters maximum
}
},
"payment": { // All payment information optional
"token_id": "7654321",
"cc_holder": "<AES_ENCRYPTED_CC_HOLDER_NAME>", // AES encrypted, then URL encoded
"cc_exp_date": "<AES_ENCRYPTED_CC_EXP_DATE>", // Must be two digits for the month, a slash, and four-digits for year, e.g. 01/2019 | AES encrypted, then URL encoded | Value should always be 12/2099 for PayPal
"cc_type": "1" // The credit card type is 1 = Visa, 2 = MasterCard, 3 = American Express, 4 = Discover
},
"products": [
{
"product": "123456789",
"sku": "123456789",
"subscription_info": { // Only use this portion of the request for cartless subscription creation
"price": "2.00", // Only use this for subscription price lock
"quantity": "2", // Used to set quantity for subscription creation
"initial_order_offset_days": "8", // Used to offset the initial order. A positive integer extends the first order date while a negative integer can be used to shorten the next order date.
"tracking_override": { // Only use this portion of the request for cartless subscription creation
"offer": "903ecf3e5efc12e49d61bc764e106cf6",
"every": "1",
"every_period": "3" // 1 = Day 2 = Week and 3 = Month
}
},
"purchase_info": {
"quantity": "2",
"price": "2.00", // price per unit
"discounted_price": "1.90", // price per unit after discount
"total": "3.80" // discounted_price * quantity
}
}
]
}
Response
If a successful connection is made, sending a POST request tohttps://sc.ordergroove.com/subscription/createwill always result in a secure HTTP Response object with code 201 and a JSON payload:
Success - Request Received
{"result": "Subscription request received", "subs_req_id": "59f215d4b3ade33e68c8ae9b"}
Error Responses
Given the amount of data being provided and the order in which the contents of the payload are validated and created, there will be instances where a 207, 400, 401, or 409 status will be returned, but the customer, shipping, and payment Ordergroove IDs will be provided as they were created, even though there may have been an issue with some or all products provided in the request.
- 207 – Request contained multiple subscriptions and some were successfully created while others were not.
- 400 – Invalid request. No subscriptions were created. Provides details about what specifically was missing or invalid in the request.
- 401 – Authentication failed.
- 409 – Conflict: A request with the given merchant_order_id was already received and processed.
If there are any errors, they will be found in the errors object of the response. You can see examples of different errors in the API documentation here:https://developers.ordergroove.com/#composite-endpoints-purchase-post-post/(Use your staging dashboard credentials to access documentation).
Error Logging and Retry Logic
It is recommended that you have email notifications tied to the 400, 401, and 409 error responses. If an issue with the POST can be identified and corrected within 24 hours, it should be resent to Ordergroove. If multiple POSTs are returning the same response, it likely points to a larger issue with the integration that should be investigated. Any retries of 409 response will need to have the merchant_order_id field modified.
In the event that you receive a 500 level error, it is recommended that you re-send the POST 3 times within the next 10 minutes. If there is still no successful connection, you should retry the POST again after 24 hours. After 24 hours, no more retries should be attempted as the contents of the customer's cart are no longer reliable.
Order Placement
In order to be able to receive recurring subscription orders, you must be able to ingest XML content containing the order information. Upon ingestion, you will essentially build the cart on your backend with the information from Ordergroove, process the order, and then provide an XML response for either a success or failure. The content sent in order XML is based on the information passed in the Purchase POST as well as any incentives that Ordergroove powers for the program.
Not all fields within the XML need to be used to create an order. For example, you may choose to disregard the Order Subtotal Value, and focus solely on the Order Total Value field. Ordergroove can send orders as often as every hour on the hour. When selecting a merchant's daily order placement hour, we try to avoid having hours 2 and 15 (CST) as the first order placement hour per day, as those can interact poorly with our release schedule and daylight savings time.
API Method
To use the API method, you need to expose an endpoint to which we will send the order information via an HTTPS POST. There is one request/response pair per order.
Batch Method
Ordergroove also supports batching orders together into a file and dropping it on the OG SFTP drop site so you can process the orders at your leisure. OG will supply you with the necessary credentials for access to the drop site. Once you have retrieved the order file you will need to remove/delete it from the SFTP. The drop site is also where you would drop off the respective response files containing the results of orders you have processed.
Filename Structures:
- Batch request filename: <MERCHANT_ID>_batch_orders_MM-DD-YYYY_HHMMSS.xml
- Batch response filename: <MERCHANT_ID>.BatchResponseMM-DD-YYYY_HHMMSS.xml
NOTE
The end of the request and response filenames are time-stamped to ensure that no files are overwritten.
Example XML Request
<?xmlversion="1.0"encoding="UTF-8"?>
<order>
<head>
<orderOgId>17071718</orderOgId> // Recurring OG Order ID
<orderPublicId>0a52e64caa1911ecbea15e94dbd6dbdx</orderPublicId> // Recurring Public Order ID
<orderOgDate><![CDATA[2022-03-23]]></orderOgDate> // Date OG Places Order
<orderSourcePartnerId>196</orderSourcePartnerId> // Merchant OG ID
<orderSourcePartnerName><![CDATA[Pet Store]]></orderSourcePartnerName> // Merchant Name
<orderItemsCount>1</orderItemsCount>
<orderSubtotalValue>10.39</orderSubtotalValue>
<orderSubtotalDiscount>0.00</orderSubtotalDiscount>
<orderSalesTax>0.00</orderSalesTax>
<orderDiscount>2.60</orderDiscount> // Recurring Order-Level Discount
<orderShipping>0.00</orderShipping>
<orderTotalValue>10.39</orderTotalValue>
<orderCurrency>USD</orderCurrency>
<orderPaymentPublicId>3c9efe848c0511ebaf4c2ec47f35ff80</orderPaymentPublicId> // Recurring Public Payment ID
<orderPaymentDataLocation>OG</orderPaymentDataLocation>
<orderPaymentMethod>CC</orderPaymentMethod>
<orderCcType><![CDATA[Visa]]></orderCcType>
<orderCcOwner/>
<orderCcNumber/>
<orderCcExpire><![CDATA[q42FjUuxkypYzVfumFf31pIMS7y+ZfBf/PmQwRSAECE=]]></orderCcExpire>
<orderTokenId><![CDATA[ABC123456789DEF]]></orderTokenId> // Payment Token Provided by Merchant
<orderPaymentLabel/>
</head>
<customer>
<customerOgId>4778263</customerOgId> // Customer OG ID
<customerPartnerId><![CDATA[DC69410241]]></customerPartnerId> // Customer Merchant ID
<customerName><![CDATA[Nicholas Bundy]]></customerName>
<customerFirstName><![CDATA[Nicholas]]></customerFirstName>
<customerLastName><![CDATA[Bundy]]></customerLastName>
<customerEmail><![CDATA[nicholas.bundy@ordergroove.com]]></customerEmail>
<customerLocale><![CDATA[en-us]]></customerLocale>
<customerShippingFirstName><![CDATA[Nicholas]]></customerShippingFirstName>
<customerShippingLastName><![CDATA[Bundy]]></customerShippingLastName>
<customerShippingAddress><![CDATA[75 Broad St Fl 23]]></customerShippingAddress>
<customerShippingAddress1><![CDATA[75 Broad St]]></customerShippingAddress1>
<customerShippingAddress2><![CDATA[Fl 23]]></customerShippingAddress2>
<customerShippingCity><![CDATA[New York]]></customerShippingCity>
<customerShippingState><![CDATA[NY]]></customerShippingState>
<customerShippingZip><![CDATA[10004]]></customerShippingZip>
<customerShippingPhone/>
<customerShippingFax/>
<customerShippingCompany/>
<customerShippingCountry><![CDATA[US]]></customerShippingCountry>
<customerBillingFirstName><![CDATA[Nicholas]]></customerBillingFirstName>
<customerBillingLastName><![CDATA[Bundy]]></customerBillingLastName>
<customerBillingAddress><![CDATA[75 Broad St Fl 23]]></customerBillingAddress>
<customerBillingAddress1><![CDATA[75 Broad St]]></customerBillingAddress1>
<customerBillingAddress2><![CDATA[Fl 23]]></customerBillingAddress2>
<customerBillingCity><![CDATA[New York]]></customerBillingCity>
<customerBillingState><![CDATA[NY]]></customerBillingState>
<customerBillingZip><![CDATA[10004]]></customerBillingZip>
<customerBillingPhone/>
<customerBillingFax/>
<customerBillingCompany/>
<customerBillingCountry><![CDATA[US]]></customerBillingCountry>
</customer>
<items>
<item>
<publicId>53045416aae111ecb1798e2cf0ff778f</publicId> // Item OG ID
<offerPublicId>a60cd8da1f6111ea9096bc764e101db1</offerPublicId>. // Public OG Offer ID
<offerProfilePublicId>a5e467c41f6111eabd2bbc764e101db1</offerProfilePublicId> // Public OG Offer Profile ID
<qty>2</qty>
<sku><![CDATA[17550870]]></sku>
<name><![CDATA[Training Treat Pack]]></name>
<product_id><![CDATA[17550870]]></product_id>
<discount>5.20</discount> // Discount * Quantity
<finalPrice>20.78</finalPrice> // (Price * Quantity) - Discount
<price>12.99</price> // Price Per Item
</item>
</items>
</order>
NOTE
- Ordergroove will wrap applicable fields of the request in CDATA.
- Ordergroove recommends flagging recurring subscription orders within your OMS for tracking purposes.
- Order placement testing initiated by Ordergroove's backend is required in order to complete full end-to-end testing prior to launch.
Additional Order Placement Objects
In addition to the standard XML content, you may have additional objects such as bundle components or subscription extra data that you would like returned as a part of the order XML. Below is an example of what those 2 additions would look like within the <items> node.
<items>
<item>
<publicId>53045416aae111ecb1798e2cf0ff778f</publicId>
<offerPublicId>a60cd8da1f6111ea9096bc764e101db1</offerPublicId>
<offerProfilePublicId>a5e467c41f6111eabd2bbc764e101db1</offerProfilePublicId>
<type>one-time</type> // Used w/Instant Upsell
<qty>2</qty>
<sku><![CDATA[17550870]]></sku>
<name><![CDATA[Training Treat Pack]]></name>
<product_id><![CDATA[17550870]]></product_id>
<discount>5.20</discount>
<finalPrice>20.78</finalPrice>
<price>12.99</price>
<components> // Used w/Bundles
<component>
<product_id>B987654</product_id>
<sku>B987654</sku>
<qty>2</qty>
</component>
<component>
<product_id>C000000</product_id>
<sku>C000000</sku>
<qty>2</qty>
</component>
</components>
<subscription> // Used w/Headless Integration
<publicId>74ec471c8c1211eb898efe0ed7e0111f</publicId>
<startDate>2022-03-17</startDate>
<originalOrderId>977968</originalOrderId>
<every>2</every>
<everyPeriod>2</everyPeriod> // 1 = Day, 2 = Week, 3 = Month
<frequencyDays>14</frequencyDays>
<extraData> // Returned as it's received in Purchase Post
<pet_name><![CDATA[Rover]]></pet_name>
<breed><![CDATA[Great Pyranese]]></breed>
</extraData>
</subscription>
</item>
</items>
Order Placement Response
Upon placing the order, you will need to return and XML object back to Ordergroove.
API Method Success Response Example:
<?xmlversion="1.0"encoding="UTF-8"?>
<order>
<code>SUCCESS</code>
<orderId>1224</orderId>
</order>
Batch Method Success Response Example:
<?xmlversion="1.0"encoding="UTF-8"?>
<orders>
<order>
<ogOrderId>119994</ogOrderId>
<code>SUCCESS</code>
<orderId>1224</orderId>
</order>
</orders>
API Error Response Example:
<?xmlversion="1.0"encoding="UTF-8"?>
<order>
<code>ERROR</code>
<errorCode>110</errorCode>
<errorMsg>The credit card number provided is not valid.</errorMsg>
</order>
Batch Error Response Example:
<?xmlversion="1.0"encoding="UTF-8"?>
<orders>
<order>
<ogOrderId>119994</ogOrderId>
<code>ERROR</code>
<errorCode>110</errorCode>
<errorMsg>The credit card number provided is not valid.</errorMsg>
</order>
</orders>
Error Response Codes:
An order may be rejected for a number of reasons. To standardize communication to Ordergroove that a failure occurred, we use error codes.
NOTE
It is important we receive these error codes because we will react differently to each code, including sending the customer-specific email communications. If the response mapping is incorrect, the customer will receive an incorrect communication via email.
The current set of available error codes (and their definitions):
- 020 — technical issue when placing order
- 100 — invalid credit card type
- 110 — invalid credit card number
- 120 — invalid credit card expiration date
- 130 — invalid billing address
- 140 — payment declined
- 150 — PayPal issue
- 160 — Payment Declined - Do Not Retry
- 170 — No Default Card on File
- 180 — Strong Customer Authentication (SCA) Requested
- 999 — generic order processing issue - should be used for temporary errors (ie temporary stock issues)
Please use these error code numbers, but use the error message from your payment gateway or processor. The descriptions above are general categories of errors.
Ordergroove Response To Order Placement Errors:
Based on the category of error that was incurred during order placement, Ordergroove will take different actions on the order. Below is a brief description of what action Ordergroove will take in response to the main four types of order placement errors.
- Error During Order Generation/Placement - If Ordergroove is unable to successfully generate the order XML, or is unable to connect to your order placement API, the order will be retried at the next order placement time (usually the next day). These orders are retried for 90 days or the subscription frequency, whichever is shorter.
- Valid Error Response - If OG receives a valid error response from your system, the customer will be notified of the error (e.g. Credit Card issue). The order is put in a rejected state.
- Response Processing Error - If OG is unable to understand the order response from your system (batch or API), the order is not retried and is put in a rejected state. The customer will not be notified of their order status.
- Generic Error Response (999) - If you respond with an error code of 999, OG will read this as an unknown error that occurred during your order placement process. These orders will be retried 3 times (once per order placement period) and if OG continues to receive a response with an error code of 999 each time, the order will be rejected. The customer will be notified when the order is rejected. If OG does not receive any responses to the retries, the order will remain in a pending status in OG's system and the customer will not be notified.
- For a reference of what emails get triggered based on responses, please view the Transactional Program Email article for more information.
Price Lock
Ordergroove incorporates logic so that if a price changes after a customer has already received the order reminder email, the customer will receive the lower of the two prices. In order to use this logic, it is required that you pick up the final price for each item in the order XML.
Supported Browser Versions
Edge 79+
Firefox 63+
Chrome 54+
Safari 10.3+