Developing "Name Your Price" Functionality for 1C-Bitrix
The PWYW (Pay What You Want) mechanism allows a customer to enter their own amount for goods or a service — with a possible minimum threshold. Bitrix doesn't support this out of the box: the price in b_catalog_price is fixed, and the sale module doesn't accept arbitrary amounts from clients. All logic is overridden at the cart and checkout level.
Where to Store Settings
For each product participating in the mechanism, three additional parameters are needed:
-
PWYW_ENABLED— active flag (Y/N) -
PWYW_MIN_PRICE— minimum amount (can be 0) -
PWYW_SUGGESTED_PRICE— suggested amount (displayed by default)
Storage — infoblock properties of the catalog. For SKUs (trade offers), they are added via CIBlockProperty::Add() to the IBLOCK_ID of trade offers. Property types — N (number) for prices, S for flag.
Alternative — HL block PwywSettings with fields UF_PRODUCT_ID, UF_MIN_PRICE, UF_SUGGESTED_PRICE, UF_ACTIVE. Suitable when you need to manage settings in bulk from admin interface without editing each product.
Intercepting Price on Cart Add
Standard $basket->addItem() takes price from the catalog. Need to intercept before saving and substitute the client's price.
Client side: form on product page contains pwyw_price field. On submit — AJAX to custom endpoint.
Server handling:
use Bitrix\Sale\Basket;
use Bitrix\Sale\BasketItem;
use Bitrix\Main\Context;
$request = Context::getCurrent()->getRequest();
$productId = (int)$request->getPost('product_id');
$userPrice = (float)$request->getPost('pwyw_price');
// Check minimum threshold
$minPrice = getPwywMinPrice($productId); // reads infoblock property
if ($userPrice < $minPrice) {
echo json_encode(['error' => 'Price below minimum']);
die();
}
$basket = Basket::loadItemsForFUser(
\CSaleBasket::GetBasketUserID(),
\Bitrix\Main\Context::getCurrent()->getSite()
);
$item = $basket->createItem('catalog', $productId);
$item->setFields([
'QUANTITY' => 1,
'CURRENCY' => \Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => SITE_ID,
'PRODUCT_PROVIDER_CLASS' => 'CCatalogProductProvider',
'PRICE' => $userPrice,
'BASE_PRICE' => $userPrice,
'CUSTOM_PRICE' => 'Y', // prevent auto-recalculation
]);
// Save entered price in basket property
$item->getPropertyCollection()->setProperty([
'NAME' => 'PWYW_PRICE',
'CODE' => 'PWYW_PRICE',
'VALUE' => $userPrice,
'SORT' => 100,
]);
$basket->save();
The CUSTOM_PRICE = 'Y' field is key. Without it, Bitrix will replace the entered price with the catalog price via CCatalogProductProvider when recalculating the cart.
Protection Against Recalculation
Even with CUSTOM_PRICE = 'Y', some handlers can reset the price. Subscribe to the OnSaleBasketItemRefreshData event and restore the price from the cart property:
AddEventHandler('sale', 'OnSaleBasketItemRefreshData', function(&$fields) {
$basketItem = $fields['BASKET_ITEM'];
$props = $basketItem->getPropertyCollection();
$pwywProp = $props->getItemByCode('PWYW_PRICE');
if ($pwywProp && $pwywProp->getValue() > 0) {
$fields['PRICE'] = (float)$pwywProp->getValue();
$fields['BASE_PRICE'] = (float)$pwywProp->getValue();
$fields['CUSTOM_PRICE'] = 'Y';
}
});
Minimum Price and UI Validation
The minimum threshold is checked twice: on the client (JavaScript) and server (PHP). Client check is for UX, server is mandatory.
On the product page, minimum price data is passed via data attributes or inline JS:
<div class="pwyw-widget"
data-min-price="{{ $minPrice }}"
data-suggested="{{ $suggestedPrice }}">
<input type="number" name="pwyw_price"
min="{{ $minPrice }}"
value="{{ $suggestedPrice }}"
step="1">
<div class="pwyw-hint">Minimum amount: {{ $minPrice }} ₽</div>
</div>
Instead of Bitrix template engine — values are substituted in the component via $arResult or $arParams.
Display in Order and Personal Account
In the administrative order (sale.admin.order.edit) and in emails, show that the price was entered manually. Override the bitrix:sale.order.ajax component template — add output of the PWYW_PRICE property with a note "price specified by buyer".
In email templates like SALE_NEW_ORDER, add a block outputting cart properties via #BASKET_ITEMS# — the PWYW_PRICE property will already be included in standard substitutions.
Analytics and Minimum Amounts
To analyze real buyer behavior — what amounts they enter, how often below minimum — log all attempts in custom table b_pwyw_log:
CREATE TABLE b_pwyw_log (
ID INT AUTO_INCREMENT PRIMARY KEY,
PRODUCT_ID INT,
USER_ID INT,
PRICE_ENTERED DECIMAL(10,2),
MIN_PRICE DECIMAL(10,2),
ACCEPTED TINYINT(1),
DATE_ADD DATETIME DEFAULT CURRENT_TIMESTAMP
);
Data from this table helps decide whether to adjust the minimum threshold.
Implementation Timeline
| Option | Description | Timeline |
|---|---|---|
| Basic (single field, no limits) | Infoblock properties + AJAX + cart handler | 3–5 days |
| With Minimum and Logging | + log table, server validation, UI hints | 1 week |
| Full (HL block settings, analytics, SKU support) | Admin interface + analytics report | 1.5–2 weeks |







