Appearance
Gift Card Management (Admin)
Flow ID: AD-23 Module(s): gift_cards, coupons, eshop (gifts) Complexity: Medium-High Last Updated: 2026-05-21
Business Context
Gift cards are monetary vouchers purchased by customers through a dedicated checkout flow (separate from the shopping cart). On successful payment, the system generates a single-use coupon code and delivers it to the recipient via email and/or SMS. Admins manage two distinct subsystems under "gift cards": (1) gift card orders -- purchasable monetary vouchers that convert to coupon codes, and (2) promotional gifts -- free products added to orders based on configurable rules. This document covers both admin surfaces.
1. Gift Card Order Management
1.1 Admin Listing
Controller: ecommercen/gift_cards/controllers/AdvGiftCardAdminListing.php (241 lines) Auth: ADVISABLE, ADMIN, ORDERS Route: admin/giftCards (mapped via application/config/routes.php) Admin Menu: ORDERS group, icon admin.menu.gift_cards.listing.icon
The listing page is a Vue-powered SPA that fetches data via JSON endpoints on the same controller.
Endpoints (internal JSON API via ApiEndpointTrait):
| Method | Action | Description |
|---|---|---|
list() | GET with query params | Paginated, filtered list (50 per page) |
get($orderId) | GET | Single gift card order detail |
cancelGiftCard($orderId) | POST | Cancel a gift card order |
acceptGiftCard($orderId) | POST | Accept/complete a gift card order |
scheduleEmailJob($orderId) | POST | Queue email resend job |
scheduleSMSJob($orderId) | POST | Queue SMS resend job |
getUsedPayways() | GET | Distinct payment methods used in orders |
Available Filters:
| Filter | Type | Description |
|---|---|---|
search | LIKE (OR) | Searches customer name/surname, send_to_email, send_to_phone, coupon code |
dateFrom / dateTo | Range | Created date range |
payway | Exact | Payment method used |
emailSent | Boolean | Email delivery status |
smsSent | Boolean | SMS delivery status |
status | Enum | Gift card status (Pending/Completed/Canceled) |
1.2 Status Lifecycle
Defined in ecommercen/gift_cards/libraries/GiftCardStatus.php (Spatie Enum):
| Status | Value | Description |
|---|---|---|
| Pending | 1 | Payment initiated but not confirmed |
| Completed | 10 | Payment confirmed, coupon generated |
| Canceled | 11 | Order canceled (manual or auto-timeout) |
Status filtering uses composite logic in AdvGiftCardOrdersModel::applyStatusFilter():
- Pending:
canceled_at IS NULL AND completed_at IS NULL - Completed:
completed_at IS NOT NULL AND canceled_at IS NULL - Canceled:
canceled_at IS NOT NULL
1.3 Accept Flow (Admin Action)
When an admin accepts a pending gift card order (acceptGiftCard()):
- Loads the gift card order record
- Creates a coupon via
coupons_model->insertCoupon():- Name:
GIFTCARD_{orderId} discount_price= gift card amountmax_count_usage= 1 (single use)coupon_type=CouponType::GiftCard()(value: 1)- Minimum cart rule:
total_cart_from= gift card amount - Validity: 5 years from acceptance
- Name:
- Generates a unique coupon code via
coupons_model->generateCoupons() - Updates gift card order: status=Completed, sets
completed_at, linkscoupon_id, resetsemail_sent/sms_sentto false - All within a DB transaction
- Calls
acceptPostActions($orderId)hook (empty by default, overrideable in client repos)
1.4 Cancel Flow (Admin Action)
When an admin cancels a gift card order (cancelGiftCard()):
- If Pending: Simple status update to Canceled with
canceled_attimestamp - If Completed: Transaction that deletes the linked coupon, nullifies
coupon_id, sets status to Canceled
Calls cancelPostActions($orderId) hook after cancellation.
1.5 Notification Resend
Admins can manually trigger email or SMS resend for completed gift card orders:
- Email resend: Schedules
ResendGiftCardEmailjob onQUEUE_GIFT_CARDSqueue - SMS resend: Schedules
ResendGiftCardSMSjob onQUEUE_GIFT_CARDSqueue
Both jobs validate that gift cards are enabled before executing. SMS additionally checks enabledSms setting.
2. Gift Card Settings
Controller: ecommercen/gift_cards/controllers/AdvGiftCardSettings.php (81 lines) Auth: ADVISABLE, ADMIN, ORDERS Route: admin/giftCardsSettingsAdmin Menu: SETTINGS group
Vue-powered settings page. Reads/writes via AdvGiftCardSettingsReader and Registry.
Registry Keys (GIFT_CARDS group)
| Key | Type | Description | Role Restriction |
|---|---|---|---|
ENABLED | bool | Master toggle for gift card feature | ADVISABLE only |
GIFT_CARDS | array | Predefined denomination amounts (sorted) | -- |
FREE_AMOUNT | bool | Allow custom (free-form) amounts | -- |
MIN_AMOUNT | numeric | Minimum gift card value | Required |
MAX_AMOUNT | numeric | Maximum gift card value (default 500) | Required |
SMS | bool | Enable SMS delivery | -- |
ORDER_PREFIX | string | Prefix for order serial numbers | -- |
Payment Method Configuration
Gift card payment methods are configured separately from regular checkout in Adv_settings.php (payment settings):
- Stored in Registry:
METHODS.PAYWAY_GIFT_CARDS - Only shown when
GIFT_CARDS.ENABLEDis true - Supported payment gateways (from
getGiftCardPayWays()inecommercen/helpers/eshop_helper.php:286-299):- PayPal, PayPal Advanced, Eurobank, Alpha Bank, Ethniki, Viva Wallet, Ethniki EE, XPay (Nexi XPay Greece)
- Iris is defined but commented out
- XPay is configured via the
XPAYregistry group (API_KEY,IS_PRODUCTION); see the Registry Configuration Summary table below. - Viva Wallet has a dedicated
GIFT_CARD_SOURCE_CODEregistry key for gift card transactions
3. Promotional Gift Management
Controller: ecommercen/eshop/controllers/Adv_gifts_admin.php (442 lines) Auth: ADVISABLE, ADMIN, MARKETING Route: gifts_admin
This is a separate subsystem from gift card orders. Promotional gifts are free products automatically added to customer orders based on configurable rules.
CRUD Operations
| Action | Method | Description |
|---|---|---|
| List | index($offset) | Paginated list (50/page) with session-persisted search |
| Add | add() | Create new gift rule with products, MUI, images |
| Edit | edit($id) | Update existing gift rule |
| Delete | delete($id) | Remove gift rule |
| Toggle | setGiftActivity($id, $active) | Enable/disable a gift |
| Search | search_append_item($mode) | AJAX product search for gift/requirement assignment |
Gift Data Fields
| Field | Type | Description |
|---|---|---|
rule_id | int | Gift rule type (from gift_rules_model) |
amount_from / amount_to | decimal | Cart value range trigger |
internal_name | string | Admin-only label |
date_start / date_end | date | Active date range (required) |
active | bool | Enabled/disabled |
gift_per_count | int | Number of gifts per qualifying order (1-10) |
gift_user_choice_count | int | How many gifts the user can choose |
is_promo | bool | Promotional flag |
priority | int | Evaluation priority |
remaining | int/null | Stock limit (null = unlimited) |
Requirements System
Gifts can have two types of requirements:
- Product requirements (
dom_req_ID[]): Specific products that must be in cart - Vendor requirements (
req_vendor_ids[]): Products from specific vendors must be in cart
MUI Fields (per language)
| Field | Type | Description |
|---|---|---|
description | text | Customer-facing description |
url | string | Link URL |
image | file | Primary image |
promo_image | file | Promotional image |
extra_vendor_image | file | Additional vendor-context image |
extra_product_image | file | Additional product-context image |
Images uploaded to files/gifts/ via advuploader.
Search Filters (Session-persisted)
| Filter | Description |
|---|---|
active | Active/inactive status |
text | Free text search |
giftRules | Filter by rule type |
maxStock | Maximum remaining stock |
Extension Hooks
The controller provides three empty protected methods for client repo overrides:
afterAdd($giftId)-- post-creation logicafterEdit($giftId)-- post-update logicafterDelete($giftId)-- post-deletion logic
4. Background Jobs
All gift card jobs are in the giftCards queue (QUEUE_GIFT_CARDS constant).
Scheduled Jobs (from application/config/jobs.php)
| Job | Schedule | Description |
|---|---|---|
CancelPendingGiftCards | Every 15 min | Auto-cancel stale pending orders |
SendGiftCardsToEmails | Every 5 min | Send emails for unsent completed orders |
SendGiftCardsToPhones | Every 5 min | Send SMS for unsent completed orders |
On-Demand Jobs (Admin-Triggered)
| Job | Trigger | Description |
|---|---|---|
ResendGiftCardEmail | Admin button | Resend gift card email for specific order |
ResendGiftCardSMS | Admin button | Resend gift card SMS for specific order |
CancelPendingGiftCards Logic
Configured timeout: giftCardDateTimeIntervalToDrop (default: PT180M = 3 hours, in application/config/app.php).
- Fetches pending orders older than the timeout (excluding Iris and PayPal Advanced)
- Bulk cancels standard gateway orders
- Iris orders: Queries Iris API for actual payment status; cancels only if Iris reports CANCELED, accepts if paid
- PayPal Advanced orders: Queries PayPal REST API for capture status; cancels if not COMPLETED, accepts if COMPLETED
Email Delivery
Two email templates:
- Recipient email (
application/views/main/mail/gift_card.php): Sent tosend_to_emailwith coupon code, amount, sender name, expiration date - Customer confirmation (
application/views/main/mail/gift_card_inform_customer.php): Sent to purchasing customer confirming the gift card purchase was completed
Email subjects configured in db_default_values.php:
GIFT_CARD: "You have received a gift card!" (multi-language)GIFT_CARD_INFORM_CUSTOMER: "Gift Card Purchase" (multi-language)
SMS Delivery
Handled by GiftCardSendSms library (ecommercen/gift_cards/jobs/GiftCardSendSms.php). Uses the customer's language preference for message localization. SMS provider configured via giftCardSMSProvider (default: yuboto).
5. Database Schema
gift_card_orders Table
sql
CREATE TABLE gift_card_orders (
id int(11) NOT NULL AUTO_INCREMENT,
customer_id int(11) NOT NULL,
amount decimal(11,2) NOT NULL,
currency_id int(11) NOT NULL,
currency_rate decimal(11,4) NOT NULL,
payway varchar(255) NOT NULL,
gift_card_status tinyint(1) NOT NULL,
coupon_id int(11) DEFAULT NULL,
send_to_email varchar(255) DEFAULT NULL,
send_to_phone varchar(255) DEFAULT NULL,
send_to_name varchar(255) DEFAULT NULL,
send_to_surname varchar(255) DEFAULT NULL,
message text DEFAULT NULL,
email_sent tinyint(1) NOT NULL DEFAULT 0,
sms_sent tinyint(1) NOT NULL DEFAULT 0,
created_at datetime NOT NULL,
completed_at datetime DEFAULT NULL,
canceled_at datetime DEFAULT NULL,
order_serial varchar(255) DEFAULT NULL,
tran_ticket varchar(32) DEFAULT NULL,
PRIMARY KEY (id)
);Indexes: customer_id, coupon_id, gift_card_status, currency_id, created_at, payway, composite (gift_card_status, email_sent), composite (gift_card_status, sms_sent).
Foreign key relationships (logical, not enforced):
customer_id->shop_customer.idcoupon_id->coupons.coupon_idcurrency_id->currencies.id
6. Modern Domain Layer (REST API)
The gift card order entity has a full modern domain layer under the Order context.
Domain Components
| Component | Path |
|---|---|
| Entity | src/Domains/Order/GiftCardOrder/Repository/Entity.php |
| Repository | src/Domains/Order/GiftCardOrder/Repository/Repository.php |
| RepositoryConfigurator | src/Domains/Order/GiftCardOrder/Repository/RepositoryConfigurator.php |
| WriteRepository | src/Domains/Order/GiftCardOrder/Repository/WriteRepository.php |
| Service | src/Domains/Order/GiftCardOrder/Service.php |
| WriteService | src/Domains/Order/GiftCardOrder/WriteService.php |
| WriteData | src/Domains/Order/GiftCardOrder/WriteData.php |
| Validator | src/Domains/Order/GiftCardOrder/Validator.php |
| ListRequest | src/Domains/Order/GiftCardOrder/ListRequest.php |
REST Controller
src/Rest/Order/Controllers/GiftCardOrder.php -- full CRUD with OpenAPI annotations.
| Method | Path | Description |
|---|---|---|
| GET | /rest/order/gift-card-order | Collection with filters/sorts |
| GET | /rest/order/gift-card-order/item | Single item by filter |
| GET | /rest/order/gift-card-order/{id} | Show by ID |
| POST | /rest/order/gift-card-order | Create |
| POST | /rest/order/gift-card-order/{id} | Update |
| DELETE | /rest/order/gift-card-order/{id} | Delete |
Auth policy (rest_policies.php): Backend auth, roles ADMIN + ORDERS.
Available filters: id, customerId, currencyId, giftCardStatus, couponId, payway, emailSent, smsSent, sendToEmail (partial), sendToName (partial), orderSerial (partial).
Available sorts: id, customerId, amount, giftCardStatus, createdAt, completedAt.
DI Registration
- Domain:
src/Domains/Order/container.php-- registers Repository, RepositoryConfigurator, Service, WriteRepository, Validator, WriteService - REST:
src/Rest/Order/container.php-- registers GiftCardOrder controller with service bindings
7. Payment Gateway Integration
Gift card orders integrate with payment gateways through the AdvGiftCardPage front controller (ecommercen/gift_cards/controllers/AdvGiftCardPage.php). Each gateway returns form data with a type (redirect, iframe, form) and URL.
Webhook Handling
Viva Wallet (ecommercen/webhooks/AdvViva.php): The webhook handler checks both shop_orders and gift_card_orders tables for the transaction ticket. If the order is a gift card:
transactionPaymentCreated-> records installments, callsacceptGiftCard()transactionFailed-> callscancelGiftCard()
Customer Order History Integration
Gift card orders appear alongside regular orders in the customer admin view (Adv_customers_admin.php::order_history()). The order model uses a UNION query (compileGiftOrdersSelect()) to merge gift card orders into the unified order timeline, with an is_gift_card flag to differentiate display.
8. Client Extension Points
| Hook / Override | Location | Purpose |
|---|---|---|
acceptPostActions($orderId) | AdvGiftCardAdminListing | Custom logic after accepting a gift card |
cancelPostActions($orderId) | AdvGiftCardAdminListing | Custom logic after canceling a gift card |
indexExtras() | AdvGiftCardAdminListing | Add extra data to listing page render |
afterAdd($giftId) | Adv_gifts_admin | Post-creation hook for promotional gifts |
afterEdit($giftId) | Adv_gifts_admin | Post-update hook for promotional gifts |
afterDelete($giftId) | Adv_gifts_admin | Post-deletion hook for promotional gifts |
AdvGiftCardSettingsReader | Can be overridden in client custom/ | Custom settings logic |
GiftCardSendSms | Can be overridden | Custom SMS delivery |
Registry Configuration Summary
| Group | Key | Default | Description |
|---|---|---|---|
| GIFT_CARDS | ENABLED | false | Master toggle |
| GIFT_CARDS | GIFT_CARDS | [] | Preset denomination amounts |
| GIFT_CARDS | FREE_AMOUNT | false | Allow custom amounts |
| GIFT_CARDS | MIN_AMOUNT | 0 | Minimum value |
| GIFT_CARDS | MAX_AMOUNT | 500 | Maximum value |
| GIFT_CARDS | SMS | false | SMS delivery toggle |
| GIFT_CARDS | ORDER_PREFIX | '' | Order serial prefix |
| METHODS | PAYWAY_GIFT_CARDS | [] | Enabled payment methods |
| VIVAWALLET | GIFT_CARD_SOURCE_CODE | '' | Viva source code for gift cards |
XPAY | API_KEY | '' | Nexi XPay API key (required); loaded via getXPaySettings() in ecommercen/helpers/registry_helper.php:346-356 |
XPAY | IS_PRODUCTION | false | Toggle XPay between sandbox (xpaysandbox.nexigroup.com) and production (xpay.nexigroup.com) endpoints |
Known Issues & Security Gaps
No known issues for the XPay integration at the admin layer at this time.
For XPay webhook and securityToken verification gaps, see CF-09 Payment Webhooks and IN-23 Nexi XPay Greece.
9. Related Flows
- CF-23 Gift Cards -- customer-facing purchase flow
- CF-13 Coupons -- gift cards create single-use coupons
- CF-09 Payment Webhooks -- Viva Wallet handles gift card payments
- AD-04 Customer Management -- gift card orders in customer order history
- AD-08 Coupon Management -- gift cards generate coupons with
coupon_type = GiftCard(distinct from standard coupons) - AD-09 Gift Rules -- promotional gift rules (free products in cart; distinct from monetary gift cards)
- AD-13 Settings -- payment method configuration for gift cards