iVendNextDevelopers Request a sandbox

Customer code check: a one-time code before a customer's account is used — the recipe

A till puts any customer on a sale, and with them their loyalty points, store credit and account. Retailers ask for proof that the customer is at the counter: a code texted to the member's phone before their points are spent, an account customer's code before they buy on account. This recipe adds that from outside, through the capture on customer select: the retailer writes one rule per customer group in Desk; your service answers each pick of a customer of a group that asks with a code field, after writing the code to a record of your own that the retailer's own Notification texts to the number on the customer's record; and the retailer's server holds every door that takes payment until the code the customer reads out is right. It is for an iVendNext Partner, or the CitiXsys Consulting Team.

Reference implementation: samples/s48_customer_code_check/ — Customer code check.

When to use it. A customer's account must not be used by someone who only knows their name or number, and iVendNext sends text messages through its own SMS gateway. A partner wallet that asks its own code (wallet/1, otp_required) covers only that wallet's balance; this recipe covers the customer, whatever they pay with.

What the cashier sees

On a demo site: the code field, naming the last two digits of the number the code went to

A wrong code, with the tries left

A member with no mobile number taken off the sale in the rule's words

Rules

  1. Declare two record types and the capture in your manifest (samples/s48_customer_code_check/manifest.json):

    • <PREFIX> Rule, one record per customer group, named by it ("autoname": "field:customer_group", a Link to Customer Group, unique): Switched on, A code (a Select: Ask for a code or Never ask, so a group below one that asks can be let off), Without a mobile number (a Select: Take them off the sale or Let them on) and the cashier's words (a Data field of at most 120 characters). Give System Manager the rights to edit it and your integration role read only. Whoever keeps the rules needs Select on Customer Group for the rule's lookup (check that the retailer's System Manager has it: a site can remove it, and the lookup is then refused);
    • <PREFIX> Code, one record per code, named by the till's code-check id ("autoname": "field:challenge"): the customer (a Link to Customer), their name, mobile (a Data field with the option Phone, which the retailer's Notification texts), code, the store, the POS Profile, the cashier, status (Sent or Used), sent_at and verified_at (whole seconds, UTC). Give it one permission row: your integration role's read, write and create. The install adds its own System Manager row, as every record type has: no cashier reads a code;
    • name the role <PREFIX> Integration, so the install makes it;
    • one capture subscription with "settings": {"event": "customer_select"} at your service's address, and that address alone in allowed_urls: the pick and the code come to the same address;
    • a feed registration naming your one integration user (its description at most 140 characters, which iVendNext's install holds), so the retailer's Connect again hands that user's key to your service;
    • a help address on each record type (a documentation property setter).
  2. Choose what happens when your service is down. Customer code check declares allow_and_flag: an outage of your service never stops a store selling; the customer stays on and the sale is flagged on Sales to review. A retailer that must never let a customer on without their code changes its subscription to refuse, after Connect again has sent your user's key; every pick of a customer then waits on your service. A key withdrawn or a user disabled (401 or 403) is the retailer's iVendNext unreadable too: log it apart from an outage.

  3. Check every call with the capture's secret (<app>:sub:<key>; the sample's key is on_select), and refuse a timestamp more than five minutes from yours, or longer than 12 digits.

  4. On a pick, read the rules and the group tree at the same time, with your integration user's key: the switched-on rules (GET /api/resource/<PREFIX> Rule, fields ["name","customer_group","ask","without_mobile","words"], filters [["enabled","=",1]], limit_page_length 0) and the group tree (GET /api/resource/Customer Group with ["name","lft","rgt"]). Keep them a minute. Each read has its own timeout, so reads in a row could outrun the capture's time to answer (the sample declares 5 seconds; the select allows 10). iVendNext gives your role a plain read on Customer Group; the two record types are the role's own. If either read fails, answer 503 (the down answer applies). You never need the customer record: the call carries customer.customer_group and customer.phone, read by the retailer's server from the record.

  5. Find the rule that decides. The customer's own group's rule; else the nearest group above it with a rule: the groups whose span holds the group's (lft below and rgt above), the innermost first. No rule up the tree, or Never ask: allow. One rule decides, whole: a group's own rule replaces the rules above it. A customer whose group your kept tree does not know, while the retailer has rules, answers 503 and drops what you kept.

  6. Ask, or let on. Ask on every pick: the till spends a right code's pass when the sale books, so the next sale asks again, and a pass of your own would undo that. No mobile number (the first number the record's phone holds has fewer than six digits): refuse in words, or allow, as the rule says. Otherwise make a code from a secure source (six digits), write the code record first, and only then answer {"verdict":"challenge","prompt":"…","length":6}. If iVendNext refuses the write, or it never reached iVendNext, answer 503. If it was sent whole and no answer came in time, still answer the challenge: saved, the code works; not saved, every code is refused until the check ends. Name the last two digits of the number in your prompt, so the customer can say that is not my number.

  7. On a code (event: "verify", the challenge_id and the code): read the record the id names. Not there (404), not Sent, empty, or older than ten minutes: refuse in words (the till usually speaks first: its own check ends at ten minutes and shows "This code check has ended.") The wrong code: refuse in words (the retailer's server counts the tries; three end the check). The right code, compared in constant time: write the record Used with the code cleared and verified_at, then answer {"verdict":"ok"}; if that write fails, answer 503.

  8. Tell iVendNext to lock the customer's group, and to decide about the phone. Your rule trusts the group the customer record holds and texts the number it holds, and the till's edit sheet lets a cashier change both (the retailer's cashier role saves customers): a member moved into a group that is never asked goes on with no code, and a number changed to the cashier's own sends the code there. The retailer closes the group by putting Customer Group at permission level 1 in Customize Form and giving level-1 write only to its managers (Customers by store, rule 7). The phone field the till edits (Phone Number, else Mobile No) takes the same lock, at a cost to say plainly: a customer the cashier creates at the till is then saved with no phone, and with the default customer group in Selling Settings or none, until a manager sets them (measured on the programme's bench). Put both in the setup, the phone as the retailer's choice.

  9. Tell iVendNext to switch it on for every till. The capture asks only at the POS Profiles its subscription answers: with none chosen, every one.

The text message

The code reaches the customer through iVendNext, never through your service:

Bounds

See also

Needs this serves

This page in the kit