Overview

The Coupon service provides a set of features that help you to issue discount codes to your customers. Such coupon could be issued to a specific customer and then marked as redeemed. These coupons can be used for a fixed or percentage-based reduction during checkout. Both time and multi-use codes are supported, with optional restriction on timeframes, order values, and individual customers.

With Coupon service, you can:

  • Set up complex rules for validating customer coupons
  • Issue coupons to customers based on the order value
  • Configure coupon validity using time-based ranges

Use the Coupon API to:

  • Create, update, retrieve and delete coupons
  • Redeem coupons as a customer, and anonymously
  • Validate coupons


API Reference

/{tenant}/coupons

Manages coupons for given tenant.

/{tenant}/coupons

get

Retrieves a list of coupons.

Requires scope hybris.coupon_manage.

post

Creates a coupon.

Requires scope hybris.coupon_manage.

/{tenant}/coupons/{code}

Manages single coupon for given tenant.

Depending on the coupon access restrictions, different scopes can be required to access or redeem a coupon:

  • hybris.coupon_manage allows to access (but not redeem) any coupon,
  • hybris.coupon_redeem allows to access and redeem any coupon which has not been explicitly restricted to selected customers (if you are such lucky customer, this scope will grant you access to such coupon),
  • hybris.coupon_redeem_on_behalf allows what hybris.coupon_redeem allows, plus it allows to access and redeem any coupon which is explicitely restricted to customer. In this case customerNumber query parameter must be used.
  • without any scopes it is possible to only access and redeem coupons which were granted to anonymous users.
get

Retrieves coupon details.

See /{tenant}/coupons/{code} endpoint description for information on required scopes.

put

Updates an existing coupon.

Requires scope hybris.coupon_manage.

delete

Deletes an existing coupon.

Requires scope hybris.coupon_manage.

/{tenant}/coupons/{code}/validation

Handles coupon validation.

post

Checks whether the coupon can be successfully redeemed. Performs all checks which are performed during actual redemption, but does not result in a redeemed coupon. Only the status of the validation is reported.

See /{tenant}/coupons/{code} endpoint description for information on required scopes.

Additionally, if hybris.coupon_redeem_on_behalf scope is present, the service will use the customer identification specified in the request body (customerNumber attribute) instead of the one associated with current authentication token.

/{tenant}/coupons/{code}/redemptions

Handles coupon redemption.

get

Retrieves a list of redemptions.

Requires scope hybris.coupon_manage.

post

Invalidate the coupon based on its validity scope.Redemption is not possible when the coupon has exceeded its maximum number allowed redemptions. To be redeemed a coupon needs to be already valid and not expired yet.

See /{tenant}/coupons/{code} endpoint description for information on required scopes.

Additionally, if hybris.coupon_redeem_on_behalf scope is present, the service will use the customer identification specified in the request body (customerNumber attribute) instead of the one associated with current authentication token.

/{tenant}/coupons/{code}/redemptions/{id}

Manages a single redemption for given coupon.

get

Retrieves a single redemption.

With scope hybris.coupon_manage it is possible to access any redemption. With scope hybris.coupon_redeem it is possible to access only redemption created by current user. With scope hybris.coupon_redeem_on_behalf it is possible to access redemption of behalf of customer, using the customerNumber query parameter.

delete

Deletes previously created redemption.

Requires scope hybris.coupon_manage.


Events

For more information about events, see the PubSub service documentation.

TopicDescriptionPayloadExample
hybris.coupon.redemptionRedemption.schema
{"redemptionId":"44e5e382414ee7ae27b30b72","couponCode":"WINTER-SALE-2015","customerNumber":"C0123456789","orderTotal":{"amount":49.99,"currency":"USD"},"discount":{"amount":10.0,"currency":"USD"},"orderCode":"O34DF3FFD", "issuedTo":"C9876543210", "redeemedAt":"2015-04-27T10:03:43.930Z"}


Scopes in Coupon Service

Scopes enable you to specify the permissions that are required to access resources and operations in the Coupon service.

The following table includes all the scopes supported by the Coupon service.

ScopeDescription
hybris.coupon_manageUse this scope to perform all coupon management operations, such as create, delete, update, retrieve redemption, and remove redemption. This scope, however, does not allow you to redeem a coupon.
hybris.coupon_redeemUse this scope to redeem a coupon (if it is not granted to anonymous users) and to access your own redemption of a coupon. If a coupon is restricted to particular customer, that restriction is still enforced.
hybris.coupon_redeem_on_behalfUse this scope to redeem a coupon on behalf of a customer.
no scopeUse no scope to create a coupon that can be redeemed by an anonymous user. No scope is required for such users.

For more information about the authorization and authentication used in SAP Hybris services, see Authorization. For more information about scopes, see Scopes.


Coupon Validation

This document outlines a few details related to the coupon validation:

  • Validation can be successful or failed.
  • You can validate a coupon without creating an actual redemption. This can be useful during the checkout phase to verify that the coupon code entered by the customer can be used for the order.
  • When validating a coupon, be sure to use a coupon that is valid at the time of validation. Coupon validation aligns to all the constraints that are valid for the real coupon redemption. For example, if you try to validate a coupon with an order value that is below the required value, an error code of 400 is returned.
  • A successful response includes a status code of 200, which indicates that the coupon code has been successfully validated without creating a redemption.

Validation failure

If the validation fails, the service provides a feedback message. The message contains a few details:

  • type: The type of error
  • message: More descriptive information about the error

The following table outlines possible feedback information that may be returned if the validation fails:

messagetype
Coupon is not activecoupon_not_active
Coupon has expiredcoupon_expired
Coupon has reached maximum number of redemptionscoupon_redemptions_exceeded
Current customer is not allowed to redeem the couponcoupon_redemption_forbidden
The order value is too low for the couponcoupon_order_total_too_low
Currency inconsistent with couponcoupon_currency_incorrect
Discount currency invalid inconsistent couponcoupon_discount_currency_incorrect
Discount amount exceeds allowed coupon discountcoupon_discount_amount_incorrect


Anonymous redemption

You can create a coupon that can be redeemed by an anonymous user. To do so, you need to use the allowAnonymous attribute. The allowAnonymous attribute is set to false by default. However, if the attribute is set to true, then the anonymous users can:

  • Retrieve the coupon: Use the GET method on https://api.beta.yaas.io/hybris/coupon/v1/{tenant}/coupons/{code}
  • Validate the coupon: Use the POST method on https://api.beta.yaas.io/hybris/coupon/v1/{tenant}/coupons/{code}/validation
  • Redeem the coupon: Use the POST method on https://api.beta.yaas.io/hybris/coupon/v1/{tenant}/coupons/{code}/redemptions.
Anonymous users cannot access the generated redemptions by using the GET method on the following endpoint:https://api.beta.yaas.io/hybris/coupon/v1/{tenant}/coupons/{code}/redemptions/{id}. To do so, users must be signed in.

Coupons for anonymous users cannot have restrictions for:

  • A maximum number of redemptions per customer, which is set in the maxRedemptionsPerCustomer attribute.
  • Specific customers that are able to redeem it, which is set with the restrictions.validFor attribute.

If any of these restrictions are set, an error code of 400 Bad Request is returned.

Anonymous users do not need to have any particular scope assigned. For more information on scopes, see the Scopes in the Coupon Service section of the Coupon service documentation.


Basics

Introduction

Coupon service comes with a set of endpoints to be used in several operations on coupons like creating, updating, or deleting coupons. Such endpoints are additionally extended to support more operations like validating or redeeming coupons.

This tutorial includes a few examples of methods you can use to create, update, retrieve, and delete a coupon.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service',
'/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret':clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.coupon_manage'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API client for Coupon service

API.createClient('couponService',
'/services/coupon/v1/api.raml');

Examples

Assume you have a business that supplies your customers with some seasonal products for winter and summer. You may want to encourage you customers to make a larger order by providing a discount to those who purchase goods for more than a certain value.

To achieve this goal, you can use a coupon that includes a set of properties that you can freely configure by simply providing appropriate values. You give it some name, you define the minimal order value, set the time range outside of which the coupon cannot be used to get a discount. You set up other properties that also have impact on the coupon, its scope, and validity. As always, it is best to take a look at some examples.

Create a coupon

First, we want to create a coupon. Most of the properties are self-descriptive and obvious. You name your coupon, give it some description so that other users have better insight in what the coupon is for. You also add some other information, for example:

  • Who the coupon is valid for, so that only particular customers can use it
  • When the coupon can be used
  • Can the anonymous customers get the discount
response = couponService.tenant(tenant).coupons.post({
  'code': 'WINTER_SALE',
  'name': 'Winter Sale',
  'description': 'Great Winter Discount of 25 USD in December and January for all Orders over 50 USD',
  'discountType': 'ABSOLUTE',
  'discountAbsolute': {
    'amount': 25,
    'currency': 'USD'
  },
  'allowAnonymous': false,
  'maxRedemptions': -1,
  'maxRedemptionsPerCustomer': -1,
  'restrictions': {
    'validFor': [
      'C0123456789',
      'C0123456788'
    ],
    'validFrom': '2016-12-01T00:00:00.000Z',
    'validUntil': '2017-01-31T23:59:59.999Z',
    'minOrderValue': {
      'amount': 50,
      'currency': 'USD'
    }
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

As much as self-descriptive the properties are, the code property needs a few words of explanation:

  • If you do not add the coupon code property while creating a new coupon, the service will generate a new, random code for that coupon.
  • If you specify the code property explicitly, the successfully created coupon will include just this value.

Response payload contains the id and link properties:

  • You did not create the id attribute, but it is here. The id attribute is auto-created and unique in the tenant for each coupon object.
  • The link property contains simply an http link to the coupon resource.

To make next calls simple and the examples clean, we assign the code of the returned coupon to a variable.

couponCode_1 = response.body.id;

assert.equal(couponCode_1, 'WINTER_SALE');
Briefly about restrictions

You can restrict any coupon you create to a specific customer or multiple customers using the validFor property. If a value is set for the validFor property, each position in the array contains a customer number. This restriction defines which customers can retrieve or redeem the coupon.

  • A coupon can be restricted to a single customer or multiple customers.
  • The customer must be signed in as one of the customers to which the coupon is restricted in order to retrieve or redeem the coupon.
  • If the customer tries to retrieve or redeem a coupon anonymously or as a different customer, an error code of 403 FORBIDDEN is returned.

As a result, the coupon restrictions define which customer (or customers, in case of multiple entries in the validFor property) are able to retrieve or redeem the coupon. Our newly created coupon includes the following restrictions:

"restrictions": {
    "validFor": {
        "C0123456789"
        "C0123456788"
    },
  "validFrom": "2016-12-01T00:00:00.000Z",
  "validUntil": "2017-01-31T23:59:59.999Z",
  "minOrderValue": {
      "amount": 50,
      "currency": "USD"
  }
}

You can also time-restrict your coupon by using validFrom and validUntil properties. You can also define minimum order value for the coupon to be valid.

Retrieve a coupon

In this paragraph you will see how you can retrieve a single coupon. Let’s say you want to retrieve the one you created in previous step. The GET method needs to know the code of this coupon. In response you will get this coupon's data.

response = couponService.tenant(tenant).coupons.code(couponCode_1).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.code, couponCode_1);

assert.equal(response.body.discountAbsolute.amount, '25');
assert.equal(response.body.restrictions.minOrderValue.amount, '50');

response;

A few notes about retrieved coupon

The range of information that is returned depends on the scope used for GET operation. The returned document complies with the Coupon schema. The data returned is mostly the same as the data passed when creating a coupon. One notable difference is the status attribute, which is evaluated based on the coupon restrictions and the current coupon state. The valid states are:

  • INACTIVE: The coupon is only valid in the future. The validFrom date has not been reached yet.
  • VALID: The coupon can be used. The maxRedemptions value has not been reached, and the coupon is within its validity period.
  • EXPIRED: The coupon validity period has expired. The validUntil date has passed.
  • USED: The maximum number of redemptions for the coupon has been reached.

Retrieve all coupons

To see how to retrieve multiple coupons, we need to create an additional one to make our example more clear. Let's create one more coupon now.

response = couponService.tenant(tenant).coupons.post({
  'code': 'SUMMER_SALE',
  'name': 'Summer Sale',
  'description': 'Great Summer Discount of 44.99 USD in July and August for all Orders over 75 USD',
  'discountType': 'ABSOLUTE',
  'discountAbsolute': {
    'amount': 44.99,
    'currency': 'USD'
  },
  'allowAnonymous': false,
  'maxRedemptions': -1,
  'maxRedemptionsPerCustomer': -1,
  'restrictions': {
    'validFor': [
      'C0123456789',
      'C0123456788'
    ],
    'validFrom': '2016-07-01T00:00:00.000Z',
    'validUntil': '2016-08-31T23:59:59.999Z',
    'minOrderValue': {
      'amount': 75,
      'currency': 'USD'
    }
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

The code of our second coupon gets stored in its own variable:

couponCode_2 = response.body.id;

assert.equal(couponCode_2, 'SUMMER_SALE');

Now, having two coupons we can run a method designed for retrieving all available coupons. We should now get the list of two coupons. However, if you have more coupons, you will retrieve the list of all coupons (paging functionality applies, by default only the first page of coupons is returned).

response = couponService.tenant(tenant).coupons.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);

assert.equal(response.body[0].code, couponCode_1);
assert.equal(response.body[1].code, couponCode_2);


response;

If you have high number of coupons in response, they may be grouped in pages. With use of query parameters, you can target results in more specific way:

  • pageSize: Use it to configure number of coupons to be returned in a single page
  • pageNumber: Use it to set the number of page to be retrieved

Update a coupon

Sometimes you may want to change some conditions your customers must meet in order to get a discount. For example, you could decide to change the discount and minimum value of an order. Let's modify discountAbsolute.amount and minOrderValue.amount properties. Only if customers meet those new criteria, they will be eligible to get a discount, and such updated coupon will only be redeemable by them.



response = couponService.tenant(tenant).coupons.code(couponCode_1).put({
  'code': 'WINTER_SALE',
  'name': 'Winter Sale',
  'description': 'Great Winter Discount of 35 USD in December and January for all Orders over 70 USD',
  'discountType': 'ABSOLUTE',
  'discountAbsolute': {
    'amount': 35,
    'currency': 'USD'
  },
  'allowAnonymous': false,
  'maxRedemptions': -1,
  'maxRedemptionsPerCustomer': -1,
  'restrictions': {
    'validFor': [
      'C0123456789',
      'C0123456788'
    ],
    'validFrom': '2016-12-01T00:00:00.000Z',
    'validUntil': '2017-01-31T23:59:59.999Z',
    'minOrderValue': {
      'amount': 70,
      'currency': 'USD'
    }
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);

response;

Coupon should now be updated. To be sure, however, retrieve it and check the updated properties: discountAbsolute.amount and minOrderValue.amount.

response = couponService.tenant(tenant).coupons.code(couponCode_1).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.code, couponCode_1);

assert.equal(response.body.discountAbsolute.amount, '35');
assert.equal(response.body.restrictions.minOrderValue.amount, '70');



response;

Delete a coupon

When you no longer need a coupon, you can delete it. This is a soft delete operation, the coupon data is not removed completely, it stays in the system and the coupon code is therefore not reusable. The operation is trivial and involves one call as shown in the following example.

response = couponService.tenant(tenant).coupons.code(couponCode_1).delete({
  }, {
        headers: {
          'Authorization': 'Bearer ' + access_token,
          'Content-Type' : 'application/json'
        }
      }
  )

assert.equal(response.status, 204);

response;

A few notes about deleted coupons

The coupons are marked as deleted, but they are not removed. This could be referred as "soft delete." The main characteristics of this approach are:

  • When you delete a coupon, it does not get removed. Instead, the coupon is flagged as deleted through a dedicated property: deleted.
  • Deleted coupons cannot be redeemed, retrieved, or validated by customers. If customers try to access a deleted coupon, an error code of 404 NOT FOUND is returned.
  • Merchants can still retrieve a deleted coupon using the coupon code.
  • Deleted coupons are excluded from the list of retrieved coupons unless the query parameter ?showDeleted is set to true. If this is the case, then all existing coupons are retrieved, including those marked as deleted.
  • The redemptions that have already been processed are preserved.
  • If, when fetching coupons, the results are filtered using the deleted parameter, then the showDeleted query parameter is ignored.

We can now check if the deleted coupon can still be retrieved, this time with deleted field set to true:

response = couponService.tenant(tenant).coupons.code(couponCode_1).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.code, couponCode_1);

assert.equal(response.body.deleted, true);

response;


Coupon Redemptions

Introduction

You provide customers with coupons they can redeem. For example, you can provide a customer with a discount at the store or other kind of savings. Customers can redeem a coupon by entering its code in the storefront application.

There are several things to note when it comes to coupons and redemptions:

  • You can let many customers use the same coupon, but you also can restrict a coupon to one particular customer
  • You can specify if only logged in customers should be able to redeem a specific coupon, or if anonymous customers are allowed also
  • One coupon can be redeemed multiple times, but you can also set a limit for the number of possible coupon redemptions
  • Coupon can have other restrictions that define in what situations it can be redeemed. Later on, in tutorial examples, you will see how the redemption can be created, how you can retrieve existing redemptions, and how to remove a redemption.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service',
'/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessTokenManageCoupon = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret':clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.coupon_manage'
});


AccessTokenRedeemCoupon = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret':clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.coupon_redeem_on_behalf'
});



AccessTokenCustomer = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret':clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant
});

To make the calls simple and the code clean, assign the access tokens to proper variables:

access_token_manage_coupon = AccessTokenManageCoupon.body.access_token;
access_token_redeem_coupon = AccessTokenRedeemCoupon.body.access_token;
access_token_customer = AccessTokenCustomer.body.access_token;

Create API client for Coupon service and Customer service

API.createClient('couponService',
'/services/coupon/v1/api.raml');
API.createClient('customerService', '/services/customer/v1/api.raml');

Examples

Our examples present two different redemption scenarios. In the first one, it is shown how a merchant can redeem a coupon on behalf of a customer. The second scenario exemplifies how a logged in customer coupon redemption API call would look like.

Redeem a coupon on behalf of a customer

A coupon can be redeemed only if it is valid and not expired. Let's briefly outline this scenario:

  • Create a new coupon. The response contains the id and link.
  • Validate the newly created coupon
  • Redeem a coupon on behalf of a customer

Let's create a coupon that will validated, and redeemed.

response = couponService.tenant(tenant).coupons.post({
  'code': 'WINTER_SALE',
  'name': 'Winter Sale',
  'description': 'Great Winter Discount of 25 USD in December and January for all Orders over 50 USD',
  'discountType': 'ABSOLUTE',
  'discountAbsolute': {
    'amount': 25,
    'currency': 'USD'
  },
  'allowAnonymous': false,
  'maxRedemptions': -1,
  'maxRedemptionsPerCustomer': -1,
  'restrictions': {
    'validFor': [
      'C0123456789',
      'C0123456788'
    ],
    'validFrom': '2015-12-01T00:00:00.000Z',
    'validUntil': '2020-01-31T23:59:59.999Z',
    'minOrderValue': {
      'amount': 50,
      'currency': 'USD'
    }
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 201);
couponCode_1 = response.body.id;

The validation step checks if the coupon can be redeemed, without actually redeeming it. To validate or redeem a coupon, at least the following mandatory information needs to be included:

  • discount: Amount and currency of the discount
  • orderTotal: Amount and currency of the total order value

In addition, when a coupon is redeemed on behalf of a customer, the payload needs to include the customer identification also:

  • customerNumber: The customer number on behalf of which the redemption is done
Coupon validation performs the same checks as coupon redemption, but does not result in redeeming the coupon. For example, if you try to validate a coupon with an order total value that is below the required minimum order value, an error code is returned. If the redemption fails due to validation errors, you can retrieve coupon details on behalf of your customers to provide them with more information.

If successful, validation only returns the status code 200. Let's validate our coupon now, before we proceed with the redemption.

response = couponService.tenant(tenant).coupons.code(couponCode_1).validation.post({

'customerNumber': 'C0123456789',
'orderTotal': {
  'currency': 'USD',
  'amount': 50
},
'discount': {
  'currency': 'USD',
  'amount': 25
   }
}, {
  headers: {
      'Authorization': 'Bearer ' + access_token_redeem_coupon,
      'Content-Type' : 'application/json'
    }
 }
)
assert.equal(response.status, 200);

response;

The coupon is now validated. Sometimes you may just want to validate it only, without creating an actual redemption for the coupon. This can be useful, for instance during the checkout phase to verify that the coupon code entered by the customer can be used for the order. In our example, however, we proceed further.

Coupon is valid and can be redeemed. But that's not all. There are other factors here you need to consider, too. Redemption will not be possible if the coupon has exceeded its maximum number of allowed redemptions. The maximum number of redemptions allowed for a coupon can be set per tenant or per customer using maxRedemptions and maxRedemptionsPerCustomer properties respectively. Note that in our case the number is -1, which means there is no limit and the coupon can be redeemed any number of times. On the other hand, if you decide your coupon should only be redeemed 100 times, you can include the maxRedemptions property in the payload and set its value to 100. If a customer attempts to redeem the coupon that was already redeemed 100 times, an error message is returned.

Let's redeem our coupon now:

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.post({

  'customerNumber': 'C0123456789',
  'orderTotal': {
    'currency': 'USD',
    'amount': 50
  },
  'discount': {
    'currency': 'USD',
    'amount': 25
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_redeem_coupon,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 201);
response;

After you have successfully created a redemption, the response includes the redemption id and link to the redemption. Let's store the id, so we can use it later to present how to retrieve this redemption:

couponRedemptionId_1 = response.body.id;

Retrieve all redemptions

Now, you have created a coupon and you also have redeemed it for your customer. Of course, in real life you may have many coupons and you may have many redemptions per coupon. And you can simply retrieve all redemptions that exist for a certain coupon. The result contains an array of objects, where each array item is a redemption object.

At this point, we have one coupon and one redemption. For the better clarity of this example, let's create additional redemption for our coupon now.

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.post({

  'customerNumber': 'C0123456788',
  'orderTotal': {
    'currency': 'USD',
    'amount': 50
  },
  'discount': {
    'currency': 'USD',
    'amount': 25
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_redeem_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 201);
response;

The id of this redemption is stored in the variable, too:

couponRedemptionId_2 = response.body.id;

Now, we are going to retrieve all redemptions for a coupon. Since we have two redemptions the result should be an array containing two items, each of them representing one redemption.

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 200);

assert.equal(response.body.length, 2);

assert.equal(response.body[0].customerNumber, 'C0123456789');
assert.equal(response.body[0].id, couponRedemptionId_1);
assert.equal(response.body[1].customerNumber, 'C0123456788');
assert.equal(response.body[1].id, couponRedemptionId_2);

response;

You might have noticed a property in the response that you have not added in the coupon nor in coupon redemption. The Coupon service, however, adds this property automatically: redeemedAt. The value is based on the server time and it provides the specific information about time the coupon has been redeemed.

Retrieve specific redemption

While you run your business longer time, you may have many coupons and many redemptions per coupon. However, you do not always need the full list of redemptions. Sometimes you may want to see the details of one particular redemption. In such case, you can retrieve a single redemption by using its id as url path parameter. Take a look at the following sample to see how it works.

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.id(couponRedemptionId_2).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 200);

assert.equal(response.body.customerNumber, 'C0123456788');
assert.equal(response.body.id, couponRedemptionId_2);

response;

Delete specific redemption

You can only remove one redemption at a time. Let's remove our second redemption now:

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.id(couponRedemptionId_2).delete({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 204);

response;

A few notes:

  • Merchants can delete a redemption assigned to both existing coupons and deleted coupons (coupons that are flagged as deleted).
  • In case of redemptions, there is no soft-delete mechanism. Deleting a redemption permanently removes it from the coupon. Each time a redemption is removed, the redemptionCount property of the coupon is decreased.

You may want to check if the last redemption was removed. The easiest way to do it here is to retrieve all redemptions again. Previously, we got an array with two elements. Now, when one of those redemptions is removed, only one item should be returned.

response = couponService.tenant(tenant).coupons.code(couponCode_1).redemptions.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);

assert.equal(response.body.length, 1);
assert.equal(response.body[0].customerNumber, 'C0123456789');
assert.equal(response.body[0].id, couponRedemptionId_1);

response;

Redemption by a customer

Customers can redeem a coupon themselves if the coupon is valid and not expired.

An anonymous customer can redeem a coupon only if the coupon allowAnonymous property is set to 'true'.

In our example we will look at coupon redemption performed by a logged in customer. Let's signup as a new customer:

customerServiceResponse = customerService.tenant(tenant).signup.post({
  "email": "yaasCustomer@yaastest.com",
  "password": "secret"
}, {
    headers: {
      'Authorization': 'Bearer ' + access_token_customer,
      'Content-Type': 'application/json'
      }
});

assert.equal(customerServiceResponse.status, 201);

loginResponse = customerService.tenant(tenant).login.post({
  "email": "yaasCustomer@yaastest.com",
  "password": "secret"
}, {
    headers: {
      'Authorization': 'Bearer ' + access_token_customer,
      'Content-Type': 'application/json'
    }
  })

assert.equal(loginResponse.status, 200);

customerToken = loginResponse.body.accessToken;

customerServiceResponse = customerService.tenant(tenant).me.get({
}, {
    headers: {
    'Authorization': 'Bearer ' + customerToken,
    'Content-Type': 'application/json'
    }
});

customerId = customerServiceResponse.body.customerNumber;

Having a customer which is logged in now, let's create a coupon that our customer is going to redeem in the next step.

response = couponService.tenant(tenant).coupons.post({
  'code': 'SUMMER_SALE',
  'name': 'Summer Sale',
  'description': 'Great Winter Discount of 25 USD in June and July for all Orders over 75 USD',
  'discountType': 'ABSOLUTE',
  'discountAbsolute': {
    'amount': 25,
    'currency': 'USD'
  },
  'allowAnonymous': false,
  'maxRedemptions': -1,
  'maxRedemptionsPerCustomer': -1,
  'restrictions': {
    'validFrom': '2015-12-01T00:00:00.000Z',
    'validUntil': '2020-01-31T23:59:59.999Z',
    'minOrderValue': {
      'amount': 75,
      'currency': 'USD'
    }
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + access_token_manage_coupon,
        'Content-Type' : 'application/json'
      }
    }
)
assert.equal(response.status, 201);

We store the coupon id in a variable to be used as a path parameter in redemption example.

couponCode = response.body.id;

Finally, let's see how the customer redeems a coupon:

response = couponService.tenant(tenant).coupons.code(couponCode).redemptions.post({

    'orderTotal': {
    'currency': 'USD',
    'amount': 75
  },
  'discount': {
    'currency': 'USD',
    'amount': 25
  }
}, {
      headers: {
        'Authorization': 'Bearer ' + customerToken,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 201);
response;


Glossary

TermDescription
couponAn item that can be offered to customers to provide discounts or other benefits.
redemptionThe act of using the coupon code by a customer. For example, when a customer redeems a coupon by entering a valid code, the charges can be amended to the reduced price.
restrictionA condition (or set of conditions) that can be used to limit the coupon validity based on time range or order value.
tenantA tenant is a group of users on a project sharing common access, with specific privileges to a service.


  • Send feedback

    If you find any information that is unclear or incorrect, please let us know so that we can improve the Dev Portal content.

  • Get Help

    Use our private help channel. Receive updates over email and contact our specialists directly.

  • hybris Experts

    If you need more information about this topic, visit hybris Experts to post your own question and interact with our community and experts.