Collect and Spend Bonus

Process and Settings Overview

There are two ways to gain a higher card balance, cash balance and bonus balance. Cash balance is gained when you load the card with money. Cash balance does not influence the basket value. In contrast, bonus balance is granted as a discount, and reduces the basket value. Depending on your card program configuration, the bonus balance is either applied to the same buy, or spent at a later time. Both, cash balance and bonus balance, reduce the amount to pay.

In order just to collect bonus balance, you only need to pass the card to the idents array of your Smart Transaction. The bonus is collected automatically, when the transaction is started, and the payment is executed. Only the card program must be configured for it, and the recorded revenue must be eligible for bonus. You can configure a blacklist or whitelist for articles to consider. You can even grant or forbid bonus collection for loading the card with cash balance. And you can control whether the bonus is applied to the full or to the reduced prices. More in the next sections.

How you spend bonus balance is explained in the section on payment.

Configure the Bonus Products

The best way to configure bonus articles, is in SecuOffice. You find the configuration at Loyalty » config cash register » Bonus products:

images/download/attachments/97386458/image2020-9-23_10-59-7-version-1-modificationdate-1607017030000-api-v2.png

You need to configure one bonus product for every VAT rate. This is needed to calculate the remaining VAT properly. In the most cases you need one bonus product for the full VAT rate, another one for the reduced rate, and often one for articles without VAT. Stamps are exempt from VAT in Germany, for instance.

You can also define an article number or EAN for it, but these are optional. You need an article number or EAN if you want to inlude or exclude the bonus discount itself from from the bonus collection. You would then add it to the whitelist.

Configure a Product Blacklist or Whitelist

The easiest way to configure the article filters for bonus collection is in SecuOffice. You find the configuration at Loyalty » config cash register » Black and white list:

images/download/attachments/97386462/image2020-9-23_11-13-20-version-1-modificationdate-1607017091000-api-v2.png

An article is included (whitelist) or excluded (blacklist), if either the article number or the EAN matches exactly. The description is not used for filtering, but makes it easier to maintain the list.

Also cash charges of your card, and the bonus products defined above can be applied for a bonus. This is intented, but not always wanted.

If you want to change this behavior:

  • Add the EAN 4260447149502 to the blasklist or whitelist in order to include it to or exclude it from revenue-based bonus.

  • Add your bonus articles to the blasklist or whitelist in order to include it to or exclude it from revenue-based bonus.

Spend Bonus Balance

Create the Smart Transaction

We create a Smart Transaction the same way we did it in our Getting started example. There is only one difference — the secuard used now belongs to another card program. (Or more precisely, the Merchant Card belongs to another Card Group.)

The program in our example is configured to grant a bonus of 10 % to be spent at a later buy. It will consider only the articles eligible for bonification.

Firstly we call the endpoint to create the Smart Transaction:

Request
POST /api/v2/Smart/Transactions HTTP/1.1
Host: connect-testing.secupay-ag.de
Authenticate: Bearer qb56tjj1bcvo9n2nj4u38k84lo
Content-Type: application/json
Accept: application/json
 
{
"is_demo": true,
"contract": {
"id": "GCR_9OIH9JOL8KIV5PFJBEOS2NN8TCM1DF"
},
    "idents": [
{
"type": "card",
"value": "9276004424644352"
}
],
"basket": {
"products": [
{
"id": 1,
"articleNumber": 30037,
"desc": "Sandwich Romano",
"quantity": 1,
"priceOne": 349,
"tax": 7
},
{
"id": 2,
"articleNumber": 30200,
"desc": "Fruit salad, small bowl",
"quantity": 1,
"priceOne": 249,
"tax": 7
},
{
"id": 3,
"articleNumber": 51,
"ean": "4999012345678",
"desc": "Water, mildly sprinkling, 0.25 ltr.",
"quantity": 2,
"priceOne": 99,
"tax": 19
}
]
},
"basket_info": {
"sum": 796,
"currency": "EUR"
}
}

This creates a Smart Transaction with an amount to pay of €7.96. The gross unit prices of the articles are €3.49, €2.49, and €0.99.

Monetary amounts are expressed in the smallest currency unit (e. g. Euro Cent). This is true for the articles as much as for the card details in the response.

The Smart Transaction has already added the loyalty card in the idents array.

Successful response:

Response
HTTP/1.1 200 OK
Content-Type: application/json
...
 
{
"object": "smart.transactions",
"id": "STX_2DH4N2AFK2NRDH3KQU52ZC89GWMRA2",
"merchant": {
"object": "general.merchants",
"id": "MRC_G8SE48FRHVITKILAPNCEDU0NUD55W8",
"companyname": "Secupay Test-Shop"
},
"contract": {
"object": "general.contracts",
"id": "GCR_9OIH9JOL8KIV5PFJBEOS2NN8TCM1DF"
},
"transactions": [],
"created": "2020-09-25T08:06:32+02:00",
"status": "created",
"transactionRef": null,
"merchantRef": null,
"intent": "sale",
"basket": {
"products": [
{
"id": 1,
"parent": null,
"articleNumber": "30037",
"ean": "",
"desc": "Sandwich Romano",
"quantity": 1,
"priceOne": 349,
"tax": 7,
"group": [],
"serialNumber": null,
"item_type": "article",
"reference_id": null,
"contract_id": null,
"sum": null
},
{
"id": 2,
"parent": null,
"articleNumber": "30200",
"ean": "",
"desc": "Fruit salad, small bowl",
"quantity": 1,
"priceOne": 249,
"tax": 7,
"group": [],
"serialNumber": null,
"item_type": "article",
"reference_id": null,
"contract_id": null,
"sum": null
},
{
"id": 3,
"parent": null,
"articleNumber": "51",
"ean": "4999012345678",
"desc": "Water, mildly sprinkling, 0.25 ltr.",
"quantity": 2,
"priceOne": 99,
"tax": 19,
"group": [],
"serialNumber": null,
"item_type": "article",
"reference_id": null,
"contract_id": null,
"sum": null
}
],
"texts": [],
"type": "default"
},
"basket_info": {
"sum": 796,
"gratuity": null,
"currency": "EUR"
},
"idents": [
{
"object": "smart.idents",
"id": "smi_1",
"prefix": "9276",
"name": "secucard Kundenkarte",
"type": "card",
"value": "9276004424644352",
"valid": true,
"merchantcard": {
"object": "loyalty.merchantcards",
"id": "MCD_2UXQJ84A62MENH7UWH6QXDYJSBNAA9",
"merchant": {
"object": "general.merchants",
"id": "MRC_G8SE48FRHVITKILAPNCEDU0NUD55W8"
},
"created_for_merchant": {
"object": "general.merchants",
"id": "MRC_F40KSJYW5AAJHHC93TQ6C7A8X76WO6"
},
"card": {
"object": "loyalty.cards",
"id": "CRD_22H4H5D47Q44URN2K8KTP876H6M3PM",
"cardnumber": "9276004424644352",
"created": "2015-02-20T08:51:46+01:00"
},
"cardgroup": {
"object": "loyalty.cardgroups",
"id": "CRG_GO7GQ6DHW3TJYGDF64WD16PUIDHQ8Y",
"display_name": "Stammkunde",
"display_name_raw": "Stammkunde",
"stock_warn_limit": 0,
"picture": "https://connect.secucard.com/ds_g/8e79737df1e2513db48908b342c3cc436edf501a"
},
"created_for_store": {
"object": "general.stores",
"id": "STO_3522R8MS2MSHCN7D37UVJYR4P6C4PX"
},
"is_base_card": false,
"points": 0,
"cash_balance": 200,
"bonus_balance": 20,
"balance": 220,
"last_usage": "2017-10-11T09:34:42+02:00",
"last_charge": "2017-10-11T09:34:42+02:00",
"stock_status": "inactive",
"lock_status": "unlocked",
"passcode": 0,
"expiry_date": null
}
}
],
"is_demo": true,
// ...
}

In the card details you see a special card group. There is both, a bonus balance of €0.20, and cash balance €2.00.

The card would already be able to collect bonus balance for this buy. But it would not be used to spend any cash or bonus balance so far.

Apply the Loyalty Card for Payment

Now we will tell the system to use the card balance to pay for this buy. Therefore we call the endpoint POST /api/v2/Smart/Transactions/{id}/preTransaction. It prepares the Smart Transaction to accept the card balance for the full amount, both cash and bonus balance.

Request
POST /api/v2/Smart/Transactions/STX_KJXJDUN542NR3WMRHRVU5WWM6ZQEA2/preTransaction HTTP/1.1
Host: connect-testing.secupay-ag.de
Authenticate: Bearer qb56tjj1bcvo9n2nj4u38k84lo
Accept: application/json

The call to preTransaction is not the actual payment. It is only to apply the card before the payment is executed, so that you know the total bonus balance and the remainder.

If everything is fine, the API responds with HTTP status 200 OK:

Response
HTTP/1.1 200 OK
Content-Type: application/json
...
 
{
"missing_sum": 576,
"bonus_products": [
{
"id": 4,
"articleNumber": "111000007",
"ean": "",
"quantity": 1,
"priceOne": -15,
"tax": 7,
"desc": "Bonus VAT 7%"
},
{
"id": 5,
"articleNumber": "111000019",
"ean": "",
"quantity": 1,
"priceOne": -5,
"tax": 19,
"desc": "Bonus VAT 19%"
}
]
}

At the same time the Smart Transaction is moved to status processing. The field missing_sum is also added to the Smart Transaction's basket_info, and the bonus_products to /basket/products.

Monetary amounts are expressed in the smallest currency unit (e. g. Euro Cent).

Remember that our secucard has a total balance of €2.20 for this merchant. There is a cash balance of €2.00, and a bonus balance of €0.20.

When we executed the payment, it would be spent so:

  • the full cash balance of €2.00;

  • €0.15 of the bonus balance for articles with the reduced VAT rate of 7%;

  • €0.05 of the bonus balance for articles with the reduced VAT rate of 19%.

If the amount to pay exceeds the card balance, it would redeem the cash balance first, then the bonus balance second.

The two "bonus products" are needed to lower the VAT in a proper way. They are added in the same proportions like the other articles. If half of the ordinary articles have the full VAT rate, half of the redeemed bonus balance will decrease the full VAT rate.

In our example, the buyer has to pay a missing sum of €5.76 using another payment method. If missing_sum were 0 (zero), this would not be necessary. This is discussed in detail in the section about payment.

Execute the Payment

In order to actually pay, you need to call one of the endpoints:

  • start/loyalty if there is no missing sum;

  • start/cash if the remainder is paid at the cash register (ECR) without our terminals;

  • start/cashless if the remainder is paid at the ECR with one of our terminals;

  • prepare/debit and start if the remainder is paid with SEPA direct debit;

  • prepare/creditcard and start if the remainder is paid with a credit card.

There is a remainder in our example, and we track an ECR payment:

Request
POST /api/v2/Smart/Transactions/STX_KJXJDUN542NR3WMRHRVU5WWM6ZQEA2/start/cash HTTP/1.1
Host: connect-testing.secupay-ag.de
Authenticate: Bearer qb56tjj1bcvo9n2nj4u38k84lo
Accept: application/json

If everything is fine, the API responds with 200 OK, and the updated representation of Smart Transaction:

Response
HTTP/1.1 200 OK
Content-Type: application/json
...
 
{
"object": "smart.transactions",
"id": "STX_KJXJDUN542NR3WMRHRVU5WWM6ZQEA2",
// ...
"transactions": [
{
"object": "loyalty.transactions",
"id": "LTX_CN5CGYUR9SP8RTU7ZZJDDZ4KZ30AN3"
}
],
"created": "2020-09-25T08:06:32+02:00",
"updated": "2020-09-25T08:10:36+02:00",
"status": "ok",
// ...
"receipt": [
{
"type": "separator",
"value": {
"caption": "Kundenkarte"
}
},
{
"type": "name-value",
"value": {
"name": "Kartennummer:",
"value": "9276004424644352",
"decoration": []
}
},
{
"type": "name-value",
"value": {
"name": "TA Code:",
"value": "29218141",
"decoration": []
}
},
{
"type": "space"
},
{
"type": "textline",
"value": {
"text": "Zahlung über:",
"decoration": []
}
},
{
"type": "textline",
"value": {
"text": "2,20 EUR",
"decoration": [
"important"
]
}
},
{
"type": "space"
},
{
"type": "textline",
"value": {
"text": "Umsatz erfasst:",
"decoration": []
}
},
{
"type": "textline",
"value": {
"text": "7,76 EUR",
"decoration": [
"important"
]
}
},
{
"type": "space"
},
{
"type": "textline",
"value": {
"text": "Bonus:",
"decoration": []
}
},
{
"type": "textline",
"value": {
"text": "0,77 EUR",
"decoration": [
"important"
]
}
},
{
"type": "space"
},
{
"type": "textline",
"value": {
"text": "Aktuelles Guthaben",
"decoration": []
}
},
{
"type": "textline",
"value": {
"text": "0,77 EUR",
"decoration": [
"important"
]
}
}
],
// ...
"idents": [
{
"object": "smart.idents",
"id": "smi_1",
"prefix": "9276",
"name": "secucard Kundenkarte",
"type": "card",
"value": "9276004424644352",
"valid": true,
"merchantcard": {
"object": "loyalty.merchantcards",
"id": "MCD_2UXQJ84A62MENH7UWH6QXDYJSBNAA9",
"merchant": {
"object": "general.merchants",
"id": "MRC_WVHJQFQ4JNVYNG5B55TYK748ZCHQP8"
},
"created_for_merchant": {
"object": "general.merchants",
"id": "MRC_F40KSJYW5AAJHHC93TQ6C7A8X76WO6"
},
"card": {
"object": "loyalty.cards",
"id": "CRD_22H4H5D47Q44URN2K8KTP876H6M3PM",
"cardnumber": "9276004424644352",
"created": "2015-02-20T08:51:46+01:00"
},
"cardgroup": {
"object": "loyalty.cardgroups",
"id": "CRG_4VA6040D2DWRYHJKCWXZEW24654PRH",
"display_name": "Stammkunde",
"display_name_raw": "Stammkunde",
"stock_warn_limit": 0,
"picture": "https://connect.secucard.com/ds_g/8e79737df1e2513db48908b342c3cc436edf501a"
},
"created_for_store": {
"object": "general.stores",
"id": "STO_3522R8MS2MSHCN7D37UVJYR4P6C4PX"
},
"is_base_card": false,
"points": 0,
"cash_balance": 200,
"bonus_balance": 20,
"balance": 220,
"last_usage": "2017-10-11T09:34:42+02:00",
"last_charge": "2017-10-11T09:34:42+02:00",
"stock_status": "inactive",
"lock_status": "unlocked",
"passcode": 1,
"expiry_date": null
}
}
],
"payment_method": "cash",
"trans_id": "29218143",
// ...
}

In our case the transaction has status ok now.

The receipt gives the buyer some additional information:

  • €2.20 are paid with the loyalty card;

  • €7.76 of the revenue are considered are entitled to collect new bonus balance;

  • €0.77 bonus was collected.

Why was the revenue recorded with €7.76, and not €7.96? You would need to exclude the bonus articles from bonus collection, so that the discount itself is ignored. To achieve this, you can add the bonus products to your blacklist, or leave them off your whitelist. The redeemed cash balance of €2.00 is always considered, since it is no discount on the article prices.