/
NUTRINTG Loyalty integrations - AIMIA

NUTRINTG Loyalty integrations - AIMIA

Business background:

Some of the brand sides use external CLP systems like AIMIA (AA). Middleware Loyality Platform Service is responsible for passing customers and orders data from Shopify do AIMIA platform.

Below is very simplified and high level diagram:

 

Orders:

  • Only fulfilled orders are send to AIMIA.

  • Products in AIMIA are identified by BARCODE/UPC code (12 digit code). AIMIA is responsible for updating products list on their side.

  • Products not present in AIMIA during Order are inserted there by MLPS

  • Orders only from registered Users are send to AIMIA (Guest’s orders are not being send)

  • Product Variant in Shopify = Product Item in AIMIA

Customers:

  • Customer is being enrolled with Email

  • Customer state has to be enabled (customers created only by making an order are not send)

  • Customer needs to get his AccountIDs added (CustomerID as one and Email as second AccountID)

  • Opt-Out settings are skipped for AIMIA, it is stored in SFMC.

  • Each customer has to accept marketing during account creation

Third party materials:

AIMIA

  • Documentation delivered by AIMIA (Clicking on methods names redirects you to the documentation):

    • On each method page you can find:

      • It’s detailed view with example requests

      • Possible response codes

      • Description

    • Whole documentation is available to everyone

Shopify

MongoDB:

  • Collection name for storing documents with data used in integration is middlewareLoyalityPlatformService_shopifyDomains

    • Document json which will be stored in this collection(Each field explained):

{
"_id" : "<SHOPIFY_DOMAIN>",
"wsToken" : "<TOKEN_FROM_AIMA>(AES ENC)", - provided by AIMIA
"location" : "<SHOP LOCATION IN AIMIA>", - provided by AIMIA
"clientId" : "<CLIENT ID FOR SHOPIFY API>(AES ENC)", - provided by Shopify
"clientSecret" : "<CLIENT SECRET FOR SHOPIFY API>(AES ENC)", - provided by Shopify
"hmacKey" : "<HMAC SECRET FROM SHOPIFY>(AES ENC)", - provided by Shopify
"transactionType" : "<TRANSACTION TYPE FOR ORDERS FROM AIMIA>", - provided by AIMIA
"countryId" : "1(US COUNTRY CODE FROM AIMIA>", - provided by AIMIA
"regionId" : "-1(UNKNOWN REGION ID REPRESENTATION)", - Constant value
"emailIdType" : "<EMAIL ID TYPE FROM AIMIA>", - provided by AIMIA
"customerIdType" : "<CUSTOMER ID TYPE FROM AIMIA>", - provided by AIMIA
"shopifyVariantApiUri" : "<VARIANT ENDPOINT FROM SHOPIFY API>”
}

Sensitive data is encrypted with AES

  • Collection for storing errors is called middlewareLoyalityPlatformService_errors

 

Application has 2 flows from business perspective, following descriptions contains their flow diagrams, technical overview and mappings.

 

Order Flow:

Solution:

#

Step

Description

#

Step

Description

1

Shopify sends order requests

Shopify sends order request to an endpoint orders/fulfilled. In the futer we might expand it also for orders/partially_fulfilled. Order update won’t be handled in first version.

{ "id": 820982911946154508, "email": "jon@doe.ca", "closed_at": null, "created_at": "2020-10-29T19:26:35-04:00", "updated_at": "2020-10-29T19:26:35-04:00", "number": 234, "note": null, "token": "123456abcd", "gateway": null, "test": true, "total_price": "403.00", "subtotal_price": "393.00", "total_weight": 0, "total_tax": "0.00", "taxes_included": false, "currency": "USD", "financial_status": "voided", "confirmed": false, "total_discounts": "5.00", "total_line_items_price": "398.00", "cart_token": null, "buyer_accepts_marketing": true, "name": "#9999", "referring_site": null, "landing_site": null, "cancelled_at": "2020-10-29T19:26:35-04:00", "cancel_reason": "customer", "total_price_usd": null, "checkout_token": null, "reference": null, "user_id": null, "location_id": null, "source_identifier": null, "source_url": null, "processed_at": null, "device_id": null, "phone": null, "customer_locale": "en", "app_id": null, "browser_ip": null, "landing_site_ref": null, "order_number": 1234, "discount_applications": [ { "type": "manual", "value": "5.0", "value_type": "fixed_amount", "allocation_method": "across", "target_selection": "explicit", "target_type": "line_item", "description": "Discount", "title": "Discount" } ], "discount_codes": [ ], "note_attributes": [ ], "payment_gateway_names": [ "visa", "bogus" ], "processing_method": "", "checkout_id": null, "source_name": "web", "fulfillment_status": "pending", "tax_lines": [ ], "tags": "", "contact_email": "jon@doe.ca", "order_status_url": "https:\/\/apple.myshopify.com\/690933842\/orders\/123456abcd\/authenticate?key=abcdefg", "presentment_currency": "USD", "total_line_items_price_set": { "shop_money": { "amount": "398.00", "currency_code": "USD" }, "presentment_money": { "amount": "398.00", "currency_code": "USD" } }, "total_discounts_set": { "shop_money": { "amount": "5.00", "currency_code": "USD" }, "presentment_money": { "amount": "5.00", "currency_code": "USD" } }, "total_shipping_price_set": { "shop_money": { "amount": "10.00", "currency_code": "USD" }, "presentment_money": { "amount": "10.00", "currency_code": "USD" } }, "subtotal_price_set": { "shop_money": { "amount": "393.00", "currency_code": "USD" }, "presentment_money": { "amount": "393.00", "currency_code": "USD" } }, "total_price_set": { "shop_money": { "amount": "403.00", "currency_code": "USD" }, "presentment_money": { "amount": "403.00", "currency_code": "USD" } }, "total_tax_set": { "shop_money": { "amount": "0.00", "currency_code": "USD" }, "presentment_money": { "amount": "0.00", "currency_code": "USD" } }, "line_items": [ { "id": 866550311766439020, "variant_id": 808950810, "title": "IPod Nano - 8GB", "quantity": 1, "sku": "IPOD2008PINK", "variant_title": null, "vendor": null, "fulfillment_service": "manual", "product_id": 632910392, "requires_shipping": true, "taxable": true, "gift_card": false, "name": "IPod Nano - 8GB", "variant_inventory_management": "shopify", "properties": [ ], "product_exists": true, "fulfillable_quantity": 1, "grams": 567, "price": "199.00", "total_discount": "0.00", "fulfillment_status": null, "price_set": { "shop_money": { "amount": "199.00", "currency_code": "USD" }, "presentment_money": { "amount": "199.00", "currency_code": "USD" } }, "total_discount_set": { "shop_money": { "amount": "0.00", "currency_code": "USD" }, "presentment_money": { "amount": "0.00", "currency_code": "USD" } }, "discount_allocations": [ ], "duties": [ ], "admin_graphql_api_id": "gid:\/\/shopify\/LineItem\/866550311766439020", "tax_lines": [ ] }, { "id": 141249953214522974, "variant_id": 808950810, "title": "IPod Nano - 8GB", "quantity": 1, "sku": "IPOD2008PINK", "variant_title": null, "vendor": null, "fulfillment_service": "manual", "product_id": 632910392, "requires_shipping": true, "taxable": true, "gift_card": false, "name": "IPod Nano - 8GB", "variant_inventory_management": "shopify", "properties": [ ], "product_exists": true, "fulfillable_quantity": 1, "grams": 567, "price": "199.00", "total_discount": "5.00", "fulfillment_status": null, "price_set": { "shop_money": { "amount": "199.00", "currency_code": "USD" }, "presentment_money": { "amount": "199.00", "currency_code": "USD" } }, "total_discount_set": { "shop_money": { "amount": "5.00", "currency_code": "USD" }, "presentment_money": { "amount": "5.00", "currency_code": "USD" } }, "discount_allocations": [ { "amount": "5.00", "discount_application_index": 0, "amount_set": { "shop_money": { "amount": "5.00", "currency_code": "USD" }, "presentment_money": { "amount": "5.00", "currency_code": "USD" } } } ], "duties": [ ], "admin_graphql_api_id": "gid:\/\/shopify\/LineItem\/141249953214522974", "tax_lines": [ ] } ], "fulfillments": [ ], "refunds": [ ], "total_tip_received": "0.0", "original_total_duties_set": null, "current_total_duties_set": null, "admin_graphql_api_id": "gid:\/\/shopify\/Order\/820982911946154508", "shipping_lines": [ { "id": 271878346596884015, "title": "Generic Shipping", "price": "10.00", "code": null, "source": "shopify", "phone": null, "requested_fulfillment_service_id": null, "delivery_category": null, "carrier_identifier": null, "discounted_price": "10.00", "price_set": { "shop_money": { "amount": "10.00", "currency_code": "USD" }, "presentment_money": { "amount": "10.00", "currency_code": "USD" } }, "discounted_price_set": { "shop_money": { "amount": "10.00", "currency_code": "USD" }, "presentment_money": { "amount": "10.00", "currency_code": "USD" } }, "discount_allocations": [ ], "tax_lines": [ ] } ], "billing_address": { "first_name": "Bob", "address1": "123 Billing Street", "phone": "555-555-BILL", "city": "Billtown", "zip": "K2P0B0", "province": "Kentucky", "country": "United States", "last_name": "Biller", "address2": null, "company": "My Company", "latitude": null, "longitude": null, "name": "Bob Biller", "country_code": "US", "province_code": "KY" }, "shipping_address": { "first_name": "Steve", "address1": "123 Shipping Street", "phone": "555-555-SHIP", "city": "Shippington", "zip": "40003", "province": "Kentucky", "country": "United States", "last_name": "Shipper", "address2": null, "company": "Shipping Company", "latitude": null, "longitude": null, "name": "Steve Shipper", "country_code": "US", "province_code": "KY" }, "customer": { "id": 115310627314723954, "email": "john@test.com", "accepts_marketing": false, "created_at": null, "updated_at": null, "first_name": "John", "last_name": "Smith", "orders_count": 0, "state": "disabled", "total_spent": "0.00", "last_order_id": null, "note": null, "verified_email": true, "multipass_identifier": null, "tax_exempt": false, "phone": null, "tags": "", "last_order_name": null, "currency": "USD", "accepts_marketing_updated_at": null, "marketing_opt_in_level": null, "admin_graphql_api_id": "gid:\/\/shopify\/Customer\/115310627314723954", "default_address": { "id": 715243470612851245, "customer_id": 115310627314723954, "first_name": null, "last_name": null, "company": null, "address1": "123 Elm St.", "address2": null, "city": "Ottawa", "province": "Ontario", "country": "Canada", "zip": "K2H7A8", "phone": "123-123-1234", "name": "", "province_code": "ON", "country_code": "CA", "country_name": "Canada", "default": true } } }

 

2

Middleware Loyality Platform Service validates if Shopify Domain is enrolled for AIMIA

MLPS validates if Shopify Domain is enrolled for AIMIA by looking it up in mongo database.

Collection can be named “loyalityService_AimiaShopifyDomains”. (In the future collection may be expanded by a field called “default_location”

In case when Shopify Domain is not enrolled, Request is dropped. Message about it should be logged only on DEBUG level.

3

MLPS perform further validations

MLPS performs:

  • Shopify Topic Header validation - it should be orders/create OR orders/updated

  • HmacKey validation

If either validation fails, error should be logged.

4

MLPS fetches barcodes(UPC codes) for products in order

MLPS fetches products barcodes by Shopify API or from collection in mongo (to be decieded)

5

MLPS transform message accordingly to the prepared mapping

  • Mapping for the order is attached below

  • ProcessPOSMessagePromotionOnSuccess is populated with 0

  • Product Total Point Value should be 0.00

  • Insert new products should be set to no

6

MLPS sends SOAP request to AIMIA

Used request: https://info.aimialoyalty.com/default.asp?W792

WS Security Token is stored in properties and is encrypted.

AIMIA point of view for the Order Flow(Screenshot from the documentation):

 

Mapping for Order flow:

 

Customer Flow:

 

#

Step

Description

#

Step

Description

1

Shopify sends customer request

Shopify sends customer request to an endpoint customers/create or customers/update.

{ "id": 706405506930370084, "email": "bob@biller.com", "accepts_marketing": true, "created_at": null, "updated_at": null, "first_name": "Bob", "last_name": "Biller", "orders_count": 0, "state": "disabled", "total_spent": "0.00", "last_order_id": null, "note": "This customer loves ice cream", "verified_email": true, "multipass_identifier": null, "tax_exempt": false, "phone": null, "tags": "", "last_order_name": null, "currency": "USD", "addresses": [ ], "accepts_marketing_updated_at": null, "marketing_opt_in_level": null, "admin_graphql_api_id": "gid:\/\/shopify\/Customer\/706405506930370084" }

 

2

MLPS performs Validation on Request

MLPS validates if Shopify Domain is enrolled for AIMIA by looking it up in mongo database.

Collection can be named “loyalityService_AimiaShopifyDomains”. (In the future collection may be expanded by a field called “default_location”)

In case when Shopify Domain is not enrolled, Request is dropped. Message about it should be logged only on DEBUG level.

3

MLPS perform further validations

MLPS performs:

  • Shopify Topic Header validation - it should be customer/create OR customer/updated

  • HmacKey validation

If either validation fails, error should be logged.

4

MLPS checks if member is existing in AIMIA

MLPS sends SOAP request FetchMemberCore https://info.aimialoyalty.com/default.asp?W580. Customer ID from Shopify Webhook is passed as Account ID

5

(Optional) MLPS sends EnrollMemberWithEmail

MLPS sends SOAP request EnrollMemberWithEmail https://info.aimialoyalty.com/default.asp?W608 (Has to store Internal Account ID returned by AIMIA)

6

(Optional) MLPS sends

MLPS sends SOAP request AddMemberAccountID https://info.aimialoyalty.com/default.asp?W662 Customer IDs from Shopify Webhook is passed as added Account ID

7

(Optional) MLPS sends Terms and Conditions

MLPS sends SOAP request IssueInteractionTermsAcceptance if Customer accepts marketing https://info.aimialoyalty.com/default.asp?W598

8

MLPS sends further data

MLPS sends SOAP requests as shown on following diagram from AIMIA. In case of updating customer data, MLPS has to find differences between existing customer and the one which came from Shopify and update it accordingly. Unnecessary requests can be skipped. Mapping is also attached in a file below.

AIMIA point of view for the Customer Flow(Screenshot from Documentation):

Mapping for Customer flow: