Skip to main content

Vouchers

Introduction

Vouchers are a way to apply discounts to orders. They can be used to reduce the cost of a checkout. Vouchers can be applied to the entire order or to specific products. They can also be limited to a minimum quantity of products in the checkout. Vouchers and promotions can be used together.

info

The vouchers on draft orders are applicable since v3.19.

Types

There are three types of vouchers:

  • ENTIRE_ORDER: the discount is applied on the total value of checkout
  • SPECIFIC_PRODUCT: the discount is applied on the total value of the product
  • SHIPPING: the discount is applied on the shipping price

We can also distinguish vouchers by their value type:

  • FIXED: reduces the price by a specified value.
  • PERCENTAGE: reduces the price by a specified percentage value.

The voucher can be applied to all checkout products, or only to specified ones.

There is also an option to apply the discount only to the cheapest eligible product. If the voucher specifies certain products, the discount will be applied only to the cheapest item included in the discount. If the voucher applies to all products, the discount will be applied only to the cheapest item overall.

To apply the voucher on checkout use checkoutAddPromoCode mutation. The discount will be visible both in the line prices and in the checkout.discount field. To apply the voucher on draft order use draftOrderCreate or draftOrderUpdate and pass voucherCode as an argument.

Usage limits

There are multiple ways to limit a voucher's usage, but default voucher codes can be used without any limits. To change that usageLimit can be set to limit the number of times a voucher can be used. The voucher will be unavailable for further use when the limit is reached. If the voucher has multiple codes, the limit is calculated as the sum of all codes usage.

Another way to limit the usage of a voucher is to set the applyOncePerCustomer field to true. In this case, the voucher code will be available for use only once per customer.

The voucher can be also set as single-use, when singleUse flag is set to true. In this case, each code from the voucher can be used only once and then the code will be deactivated after use. This flag can be updated on existing voucher only when no code has been used yet.

Those limitation options can be combined. For example, if usageLimit is set to 10 and applyOncePerCustomer is set to true, the voucher can be used by the first 10 users.

warning

Shuffling voucher settings might result in a mismatch between the voucher.used field and the number of orders utilizing the voucher. Saleor operates on the general assumption that updating the voucher settings should not affect existing orders if possible.

Voucher usage in draft orders

Saleor is able to calculate voucher usage in draft orders. To turn on such a behavior, includeDraftOrderInVoucherUsage flag for the given Channel must be set to true. Use channelUpdate mutation to switch the flag.

When the includeDraftOrderInVoucherUsage flag is changed from false to true, vouchers will be disconnected from all draft orders.

When the includeDraftOrderInVoucherUsage flag is changed from true to false, all vouchers will be released. It means:

  • multiple-use voucher codes will reduce their usage by one (VoucherCode.used field)
  • single-use voucher codes will become active again (VoucherCode.isActive field)
  • in case of applyOncePerCustomer setting on, the usage will be dissociated from users (VoucherCustomer entry)

Voucher code will also be released, after the draft order deletion.

When importing a draft order using bulkOrderCreate mutation, the voucher is not validated, therefore code usage will not be counted.

Permissions

Managing promotions is available for users and apps with the MANAGE_DISCOUNTS permission.

Create and update a voucher

Creating voucher

To create a voucher use voucherCreate mutation.

API allows to create voucher with multiple codes. To do that use the addCodes field to send a list of codes.

Input:

{
"input": {
"name": "NewVoucher",
"type": "ENTIRE_ORDER",
"addCodes": ["code1", "code2", "code3"],
"discountValueType": "FIXED",
"minCheckoutItemsQuantity": 10,
"applyOncePerOrder": true,
"applyOncePerCustomer": true,
"singleUse": false,
"usageLimit": 10
}
}

Mutation:

mutation voucherCreate($input: VoucherInput!) {
voucherCreate(input: $input) {
errors {
field
code
message
voucherCodes
}
voucher {
id
type
minCheckoutItemsQuantity
name
usageLimit
used
codes(first: 10) {
edges {
node {
code
used
}
}
}
discountValueType
startDate
endDate
applyOncePerOrder
applyOncePerCustomer
singleUse
}
}
}
Expand ▼

Here is the response:

{
"data": {
"voucherCreate": {
"errors": [],
"voucher": {
"id": "Vm91Y2hlcjo0",
"type": "ENTIRE_ORDER",
"minCheckoutItemsQuantity": 10,
"name": "NewVoucher",
"usageLimit": 10,
"used": 0,
"codes": {
"edges": [
{
"node": {
"code": "code3",
"used": 0
}
},
{
"node": {
"code": "code2",
"used": 0
}
},
{
"node": {
"code": "code1",
"used": 0
}
}
]
},
"discountValueType": "FIXED",
"startDate": "2023-10-23T08:20:12.215018+00:00",
"endDate": null,
"applyOncePerOrder": true,
"applyOncePerCustomer": true,
"singleUse": false
}
}
}
}
Expand ▼

Updating voucher

To update a voucher use voucherUpdate mutation.

Using this mutation voucher codes can be only added. To remove codes use voucherDelete mutation.

Input:

{
"input": {
"addCodes": ["NewCode"]
}
}

Mutation:

mutation voucherUpdate($input: VoucherInput!) {
voucherUpdate(input: $input) {
errors {
field
code
message
voucherCodes
}
voucher {
id
type
minCheckoutItemsQuantity
name
usageLimit
used
codes(first: 10) {
edges {
node {
code
used
}
}
}
discountValueType
startDate
endDate
applyOncePerOrder
applyOncePerCustomer
singleUse
}
}
}
Expand ▼

Here is the response:

{
"data": {
"voucherUpdate": {
"errors": [],
"voucher": {
"id": "Vm91Y2hlcjo0",
"type": "ENTIRE_ORDER",
"minCheckoutItemsQuantity": 10,
"name": "NewVoucher",
"usageLimit": 10,
"used": 0,
"codes": {
"edges": [
{
"node": {
"code": "NewCode",
"used": 0
}
},
{
"node": {
"code": "code3",
"used": 0
}
},
{
"node": {
"code": "code2",
"used": 0
}
},
{
"node": {
"code": "code1",
"used": 0
}
}
]
},
"discountValueType": "FIXED",
"startDate": "2023-10-23T08:20:12.215018+00:00",
"endDate": null,
"applyOncePerOrder": true,
"applyOncePerCustomer": true,
"singleUse": false
}
}
}
}
Expand ▼

Applying a voucher

Applying the entire order voucher

In the example below, the entire order voucher with a fixed discount of $5 is applied at checkout. The order consists of two lines: the first for $4 and the second for $45.

mutation {
checkoutAddPromoCode(
token: 7303902b-dda8-4357-95a0-64b83b59e2e2, promoCode: "DISCOUNT"
) {
errors {
field
message
code
}
checkout {
id
token
voucherCode
discountName
discount {
amount
}
subtotalPrice {
tax {
amount
}
gross {
amount
}
net {
amount
}
}
lines {
quantity
totalPrice {
net {
amount
}
gross {
amount
}
}
undiscountedTotalPrice {
amount
}
unitPrice {
net {
amount
}
gross {
amount
}
}
undiscountedUnitPrice {
amount
}
}
}
}
}
Expand ▼

Here is the response:

{
"data": {
"checkoutAddPromoCode": {
"errors": [],
"checkout": {
"id": "Q2hlY2tvdXQ6OTNmMWQxZjItMjBjNC00ZWMyLTkwYzgtOThjYmEzY2YyNTU1",
"token": "93f1d1f2-20c4-4ec2-90c8-98cba3cf2555",
"voucherCode": "DISCOUNT",
"discountName": "Big order discount",
"discount": {
"amount": 5.0
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 44.0
},
"net": {
"amount": 44.0
}
},
"lines": [
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 3.59
},
"gross": {
"amount": 3.59
}
},
"undiscountedTotalPrice": {
"amount": 4.0
},
"unitPrice": {
"net": {
"amount": 3.59
},
"gross": {
"amount": 3.59
}
},
"undiscountedUnitPrice": {
"amount": 4.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 40.41
},
"gross": {
"amount": 40.41
}
},
"undiscountedTotalPrice": {
"amount": 45.0
},
"unitPrice": {
"net": {
"amount": 40.41
},
"gross": {
"amount": 40.41
}
},
"undiscountedUnitPrice": {
"amount": 45.0
}
}
]
}
}
}
}
Expand ▼

The discount is visible on both the checkout.discount field and the prices in checkout.lines. In the first line, the totalPrice is $0.41 less than the undiscountedTotalPrice, while the difference in the second line is $4.59. The checkout.discount is the sum of the differences between totalPrice and undiscountedTotalPrice of the lines.

info

If the user applied a fixed-amount order voucher during checkout, and the order contains multiple lines, the discount will be distributed evenly in proportion to the total price of each line.

Applying the once-per-order entire order voucher

If a voucher with the applyOncePerOrder flag set to True is used in a similar scenario, the discount will only apply to the cheapest eligible product. In this checkout, the cheapest eligible product is priced at $4. Therefore, the discount will be $4 and will only appear on one line.

Refer to the response from running the same mutation as before. The checkout.discount is 4.0. The cheapest line's totalPrice is 0.0, and the undiscountedTotalPrice is 4.0.

{
"data": {
"checkoutAddPromoCode": {
"errors": [],
"checkout": {
"id": "Q2hlY2tvdXQ6OTNmMWQxZjItMjBjNC00ZWMyLTkwYzgtOThjYmEzY2YyNTU1",
"token": "93f1d1f2-20c4-4ec2-90c8-98cba3cf2555",
"voucherCode": "DISCOUNT",
"discountName": "Big order discount",
"discount": {
"amount": 4.0
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 45.0
},
"net": {
"amount": 45.0
}
},
"lines": [
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 0.0
},
"gross": {
"amount": 0.0
}
},
"undiscountedTotalPrice": {
"amount": 4.0
},
"unitPrice": {
"net": {
"amount": 0.0
},
"gross": {
"amount": 0.0
}
},
"undiscountedUnitPrice": {
"amount": 4.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 45.0
},
"gross": {
"amount": 45.0
}
},
"undiscountedTotalPrice": {
"amount": 45.0
},
"unitPrice": {
"net": {
"amount": 45.0
},
"gross": {
"amount": 45.0
}
},
"undiscountedUnitPrice": {
"amount": 45.0
}
}
]
}
}
}
Expand ▼

Applying the specific product voucher

In the following example, a 10% voucher for a specific product is applied during checkout. The discount applies to the first two lines, one for $45 and the other for $20, but not to the third line for $1.99. The response of running the checkoutAddPromoCode mutation with the voucher code for this discount is shown below:

{
"data": {
"checkoutAddPromoCode": {
"errors": [],
"checkout": {
"id": "Q2hlY2tvdXQ6YWJlZTQzNTEtMGZjMS00MWYzLTk1YzEtMTIyMTc4NWMwYzY2",
"token": "abee4351-0fc1-41f3-95c1-1221785c0c66",
"voucherCode": "SPECIFIC PRODUCT",
"discountName": null,
"discount": {
"amount": 6.5
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 60.49
},
"net": {
"amount": 60.49
}
},
"lines": [
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 40.5
},
"gross": {
"amount": 40.5
}
},
"undiscountedTotalPrice": {
"amount": 45.0
},
"unitPrice": {
"net": {
"amount": 40.5
},
"gross": {
"amount": 40.5
}
},
"undiscountedUnitPrice": {
"amount": 45.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 18.0
},
"gross": {
"amount": 18.0
}
},
"undiscountedTotalPrice": {
"amount": 20.0
},
"unitPrice": {
"net": {
"amount": 18.0
},
"gross": {
"amount": 18.0
}
},
"undiscountedUnitPrice": {
"amount": 20.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 1.99
},
"gross": {
"amount": 1.99
}
},
"undiscountedTotalPrice": {
"amount": 1.99
},
"unitPrice": {
"net": {
"amount": 1.99
},
"gross": {
"amount": 1.99
}
},
"undiscountedUnitPrice": {
"amount": 1.99
}
}
]
}
}
}
}
Expand ▼

As we can see, the 10% discount has been applied to the first two lines. The total discount amount is visible in the checkout.discount field, which is equal to the sum of the differences between the undiscountedTotalPrice and totalPrice of all lines.

Applying the once-per-order specific product voucher

If the voucher has the applyOncePerOrder flag set to True, the discount will only be applied to the single cheapest product eligible for the discount. In the scenario described, the discount would only be applied to the product with a price of $20, and would be visible on only one line of the order.

{
"data": {
"checkoutAddPromoCode": {
"errors": [],
"checkout": {
"id": "Q2hlY2tvdXQ6YWJlZTQzNTEtMGZjMS00MWYzLTk1YzEtMTIyMTc4NWMwYzY2",
"token": "abee4351-0fc1-41f3-95c1-1221785c0c66",
"voucherCode": "SPECIFIC PRODUCT",
"discountName": null,
"discount": {
"amount": 2.0
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 64.99
},
"net": {
"amount": 64.99
}
},
"lines": [
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 45.0
},
"gross": {
"amount": 45.0
}
},
"undiscountedTotalPrice": {
"amount": 45.0
},
"unitPrice": {
"net": {
"amount": 45.0
},
"gross": {
"amount": 45.0
}
},
"undiscountedUnitPrice": {
"amount": 45.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 18.0
},
"gross": {
"amount": 18.0
}
},
"undiscountedTotalPrice": {
"amount": 20.0
},
"unitPrice": {
"net": {
"amount": 18.0
},
"gross": {
"amount": 18.0
}
},
"undiscountedUnitPrice": {
"amount": 20.0
}
},
{
"quantity": 1,
"totalPrice": {
"net": {
"amount": 1.99
},
"gross": {
"amount": 1.99
}
},
"undiscountedTotalPrice": {
"amount": 1.99
},
"unitPrice": {
"net": {
"amount": 1.99
},
"gross": {
"amount": 1.99
}
},
"undiscountedUnitPrice": {
"amount": 1.99
}
}
]
}
}
}
}
Expand ▼

Here, we can see that the discount was only applied to the cheapest line included in the voucher discount. The total checkout.discount is $2.0 in this case. The difference between totalPrice and undiscountedTotalPrice is only visible on the second line.

Catalogue Promotion and Voucher together

Catalogue promotions and vouchers can be combined. In this case, the voucher discount is applied to the price after the promotion discount. Let's consider an example: the checkout has two items, and the first item is on $5 fixed promotion. A percentage discount of 50% is being applied to the entire order.

Here is the checkout data before applying the voucher code (only the promotion is included in the price).

{
"data": {
"checkout": {
"id": "Q2hlY2tvdXQ6ZDdjMjY5M2MtOTU0ZS00YTM4LWJkMjQtMzM1Y2NiODk0YWMy",
"email": "admin@example.com",
"token": "d7c2693c-954e-4a38-bd24-335ccb894ac2",
"channel": {
"slug": "default-channel"
},
"voucherCode": null,
"discount": {
"amount": 0.0
},
"discountName": null,
"giftCards": [],
"totalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 65.0,
"currency": "USD",
"__typename": "Money"
},
"net": {
"amount": 65.0,
"currency": "USD",
"__typename": "Money"
},
"__typename": "TaxedMoney"
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 65.0,
"currency": "USD",
"__typename": "Money"
},
"net": {
"amount": 65.0,
"currency": "USD",
"__typename": "Money"
},
"__typename": "TaxedMoney"
},
"lines": [
{
"id": "Q2hlY2tvdXRMaW5lOmNlNzJiY2IyLWJjZjYtNGQxZS04MDRmLWMwZDdjZWI2NWM3OQ==",
"quantity": 2,
"undiscountedTotalPrice": {
"amount": 40.0
},
"totalPrice": {
"gross": {
"amount": 30.0
},
"net": {
"amount": 30.0
}
},
"unitPrice": {
"net": {
"amount": 15.0
}
},
"undiscountedUnitPrice": {
"amount": 20.0
},
"variant": {
"id": "UHJvZHVjdFZhcmlhbnQ6MzQ4",
"name": "S",
"pricing": {
"price": {
"gross": {
"amount": 15.0
}
}
},
"product": {
"name": "Monospace Tee",
"chargeTaxes": false
}
}
},
{
"id": "Q2hlY2tvdXRMaW5lOjI5ZDA4Zjc2LTdlMTItNGE5OC05ZWFkLThhNzFjMDMyNGQyOQ==",
"quantity": 1,
"undiscountedTotalPrice": {
"amount": 35.0
},
"totalPrice": {
"gross": {
"amount": 35.0
},
"net": {
"amount": 35.0
}
},
"unitPrice": {
"net": {
"amount": 35.0
}
},
"undiscountedUnitPrice": {
"amount": 35.0
},
"variant": {
"id": "UHJvZHVjdFZhcmlhbnQ6MzQ2",
"name": "UHJvZHVjdFZhcmlhbnQ6MzQ2",
"pricing": {
"price": {
"gross": {
"amount": 35.0
}
}
},
"product": {
"name": "Blue Hoodie",
"chargeTaxes": false
}
}
}
]
}
}
}
Expand ▼

As we can see the price of the first line is reduced by the promotion discount. The $5 discount is applied on each line so we have $10 total discount on the fist line.

Below is the checkout data after applying the 50% entire order voucher.

{
"data": {
"checkout": {
"id": "Q2hlY2tvdXQ6ZDdjMjY5M2MtOTU0ZS00YTM4LWJkMjQtMzM1Y2NiODk0YWMy",
"email": "admin@example.com",
"token": "d7c2693c-954e-4a38-bd24-335ccb894ac2",
"channel": {
"slug": "default-channel"
},
"voucherCode": "DISCOUNT",
"discount": {
"amount": 32.5
},
"discountName": "Big order discount",
"giftCards": [],
"totalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 32.5,
"currency": "USD",
"__typename": "Money"
},
"net": {
"amount": 32.5,
"currency": "USD",
"__typename": "Money"
},
"__typename": "TaxedMoney"
},
"subtotalPrice": {
"tax": {
"amount": 0.0
},
"gross": {
"amount": 32.5,
"currency": "USD",
"__typename": "Money"
},
"net": {
"amount": 32.5,
"currency": "USD",
"__typename": "Money"
},
"__typename": "TaxedMoney"
},
"lines": [
{
"id": "Q2hlY2tvdXRMaW5lOmNlNzJiY2IyLWJjZjYtNGQxZS04MDRmLWMwZDdjZWI2NWM3OQ==",
"quantity": 2,
"undiscountedTotalPrice": {
"amount": 40.0
},
"totalPrice": {
"gross": {
"amount": 15.0
},
"net": {
"amount": 15.0
}
},
"unitPrice": {
"net": {
"amount": 7.5
}
},
"undiscountedUnitPrice": {
"amount": 20.0
},
"variant": {
"id": "UHJvZHVjdFZhcmlhbnQ6MzQ4",
"name": "S",
"pricing": {
"price": {
"gross": {
"amount": 15.0
}
}
},
"product": {
"name": "Monospace Tee",
"chargeTaxes": false
}
}
},
{
"id": "Q2hlY2tvdXRMaW5lOjI5ZDA4Zjc2LTdlMTItNGE5OC05ZWFkLThhNzFjMDMyNGQyOQ==",
"quantity": 1,
"undiscountedTotalPrice": {
"amount": 35.0
},
"totalPrice": {
"gross": {
"amount": 17.5
},
"net": {
"amount": 17.5
}
},
"unitPrice": {
"net": {
"amount": 17.5
}
},
"undiscountedUnitPrice": {
"amount": 35.0
},
"variant": {
"id": "UHJvZHVjdFZhcmlhbnQ6MzQ2",
"name": "UHJvZHVjdFZhcmlhbnQ6MzQ2",
"pricing": {
"price": {
"gross": {
"amount": 35.0
}
}
},
"product": {
"name": "Blue Hoodie",
"chargeTaxes": false
}
}
}
]
}
}
}
Expand ▼

As we can see, the total discount is $32.5.0. In the first line, the difference between totalPrice and undiscountedTotalPrice ($25) is the total discount applied to this line, which includes both promotion and voucher discounts. However, in the second line, the difference between those values ($17.5) comes from the voucher discount.

To calculate the value of the applied promotion, we can sum up the line discounts and subtract checkout.discount. In this example, the calculation would be: ($25 + $17.5) - $32.5 = $10. Therefore, we end up with the same value as before applying the voucher.

Completing checkout with a voucher discount

When completing the checkout with an assigned voucher, the applied voucher discount will be visible on the order and order line prices. Additionally, the sum of voucher discounts will be reflected in the order.discounts field. This behavior is consistent across all voucher types.

The following example shows the response from the checkoutComplete mutation for a checkout that includes two items of the same variant with a 10% voucher discount applied.

{
"data": {
"checkoutComplete": {
"order": {
"id": "T3JkZXI6NmM1MjhkNGYtZjc5YS00OGM1LTk2ZWUtYjI0M2U2ZjdmMDBm",
"status": "UNFULFILLED",
"totalCaptured": {
"amount": 113.51
},
"subtotal": {
"net": {
"amount": 33.96
},
"gross": {
"amount": 36.0
}
},
"total": {
"currency": "USD",
"net": {
"amount": 107.08
},
"gross": {
"amount": 113.51
}
},
"undiscountedTotal": {
"currency": "USD",
"net": {
"amount": 113.12
},
"gross": {
"amount": 117.51
}
},
"discounts": [
{
"name": null,
"type": "VOUCHER",
"valueType": "FIXED",
"amount": {
"amount": 4.0
}
}
],
"lines": [
{
"quantity": 2,
"totalPrice": {
"gross": {
"amount": 36.0
},
"net": {
"amount": 33.96
}
},
"unitPrice": {
"gross": {
"amount": 18.0
},
"net": {
"amount": 16.98
}
},
"undiscountedUnitPrice": {
"gross": {
"amount": 20.0
},
"net": {
"amount": 20.0
}
},
"unitDiscount": {
"amount": 2.0
}
}
]
},
"errors": []
}
}
}
Expand ▼

The discount amount can be found in order.discounts.amount, the value is equal to the order.lines.unitDiscount multiplied by the line quantity. The discount can also be seen in the line prices - compare the undiscountedUnitPrice and the unitPrice.

Webhooks

All voucher actions also generate asynchronous webhooks that can be used for reacting to the events emitted by Saleor, e.g. VOUCHER_CREATED, VOUCHER_UPDATED, VOUCHER_DELETED. The WebhookEventTypeAsyncEnum represents the full list of asynchronous webhooks.

You can learn more about webhooks here.

Exporting voucher codes

Voucher codes can be exported to a CSV or XLSX file.

You can learn more about voucher codes export here.