Wix Halkbank POS Setup Guide: Step-by-Step Secure Payment Infrastructure
Once the Wix Halkbank POS installation steps are completed, businesses can receive installment and 3D Secure supported payments.
The Importance of Proper Setup
Setting up Halkbank POS on Wix is one of the most critical processes that forms the payment infrastructure of your e-commerce store. When the correct steps are followed, this setup can be completed completely securely and quickly, but even the smallest mistake can disrupt the payment flow or create security vulnerabilities. This guide includes all the steps, code examples, and best practices you need to integrate Halkbank POS into your Wix store.
There are three main stages in the setup process: obtaining API credentials from Halkbank and adding them to Velo backend, configuring payment endpoints, and setting up the 3D Secure verification flow¹. Each stage must be completed and tested carefully. Remember that this setup is a one-time process - when done correctly, your system will work smoothly for years.
Pre-Setup: Required Information and Tools
Before starting the setup, some preparations need to be made. First, your virtual POS agreement with Halkbank must be completed. The bank will send you an email containing your basic information. However, these are not sufficient for setup - you need to access the Halkbank management panel and collect additional information yourself.
On the Wix side, Dev Mode must be active². Dev Mode is activated by clicking "Dev Mode" from the top menu of Wix Editor and selecting "Turn on Dev Mode"². When this mode is activated, the Code Panel opens and allows you to write frontend and backend JavaScript code². Without Dev Mode, you cannot use custom code and make backend API calls².
Logging into Halkbank Management Panel
Log in to https://sanalpos.halkbank.com.tr/halk/report/user.login using the credentials from the email sent by Halkbank. After logging in, your first task is to check your existing store information. Click on the "Management" tab from the left menu and open the "Member Merchant Operations" section. Note the information you see here:
Merchant Number (Merchant ID): Usually a 9-digit number, this is your unique identifier in the Halkbank system³. You will use this number in all API calls.
Terminal ID: Your terminal number, accessible from the Member Merchant Terminal Information page, is used in some payment models³.
3D Model: Check your current 3D model from the "3D Settings" section. "3D PAY" model is required for Wix integration⁴. If your model is different, contact your Halkbank customer representative to change it. The 3D PAY model allows you to take full control of the payment flow and is mandatory to use callback URLs.
Creating API User
You can log in to the Halkbank panel with your regular user account, but you need to create a special "API User" for API integration⁴. Follow the path "Management > Add New User" from the left menu. In the form that opens, select "API User" as the user type and set a username and password⁴. Keep this information in a safe place - you will use this information in your Wix backend.
After creating the API user, go to the "Change Security Key" section⁵. Note the 3D Security Key you see here. This key will be used in hash calculations and will guarantee the integrity of transaction data. If you don't see an existing key, you can create one by clicking the "Create New Key" button.
Step 1: Preparing Wix Velo Backend
After Dev Mode is active in Wix Editor, you will see the Code Panel on the left side. In this panel, you will create new files under the "Backend" folder. Backend files end with .jsw extension and only run on the server side - they are never visible or accessible in the browser⁶.
Secure Information Storage with Secrets Manager
Writing your API credentials directly into the code is a major security mistake. Wix offers a service called Secrets Manager to store such sensitive information⁷. Click on the "Site Structure" tab in the Code Panel and find the "Secrets Manager" section. Click the "Add Secret" button and add the following information in order:
Secret Name: halkbank_merchant_id Secret Value: [Your merchant number]
Secret Name: halkbank_api_usernameSecret Value: [Your API username]
Secret Name: halkbank_api_password Secret Value: [Your API user password]
Secret Name: halkbank_security_key Secret Value: [Your 3D Security key]
Secret Name: halkbank_terminal_id Secret Value: [Your Terminal ID]
The information stored in Secrets Manager is accessible in your backend code through the wix-secrets-backend module⁷. However, it is never visible in frontend code or the browser. This is the most secure method to protect your API credentials.
Creating Backend File
Right-click on the "Backend" folder in the Code Panel and select "New .jsw File". Give the file a descriptive name like halkbankService.jsw. This file will manage all your communication with Halkbank.
Import the necessary modules at the beginning of the file:
// backend/halkbankService.jsw
import { fetch } from 'wix-fetch';
import { getSecret } from 'wix-secrets-backend';
import crypto from 'crypto';
The wix-fetch module is used to make HTTP requests. The wix-secrets-backend module is needed to retrieve information from Secrets Manager, and the crypto module is required for hash calculations.
Step 2: Writing the Hash Calculation Function
Hash calculation is one of the most critical components of Halkbank POS integration. Hash is like a mathematical fingerprint of your data and guarantees that the data has not been altered in transit. The Halkbank system uses the SHA-512 algorithm for PCI DSS 4.0 compliance⁸.
async function calculateHash(orderId, amount, currency) {
try {
const merchantId = await getSecret('halkbank_merchant_id');
const securityKey = await getSecret('halkbank_security_key');
// Create data string for hash ver3
// Format: MerchantId|OrderId|Amount|Currency|SecurityKey
const dataString = `${merchantId}|${orderId}|${amount}|${currency}|${securityKey}`;
// Calculate SHA-512 hash and convert to Base64
const hash = crypto
.createHash('sha512')
.update(dataString, 'utf8')
.digest('base64');
return hash;
} catch (error) {
console.error('Hash calculation error:', error);
throw new Error('Failed to calculate hash');
}
}
This function takes order number, amount, and currency information, combines it with the security key, and calculates the SHA-512 hash. The format used during hash calculation is very important - you must strictly follow the format specified in Halkbank documentation. Otherwise, the bank cannot verify the hash and the transaction will be rejected.
Step 3: Creating Payment Initiation Endpoint
Payment initiation is the process triggered when the customer clicks the "Pay" button. At this stage, order information is collected, hash is calculated, and a POST request is sent to Halkbank API.
export async function initiatePayment(orderData) {
try {
// Retrieve information from Secrets
const merchantId = await getSecret('halkbank_merchant_id');
const apiUsername = await getSecret('halkbank_api_username');
const apiPassword = await getSecret('halkbank_api_password');
const terminalId = await getSecret('halkbank_terminal_id');
// Convert amount to kuruş (Halkbank requires amounts in kuruş)
const amountInKurus = Math.round(orderData.amount * 100);
// Calculate hash
const hash = await calculateHash(
orderData.orderId,
amountInKurus,
'949' // TRY currency code
);
// Prepare data for API request
const requestData = {
merchantId: merchantId,
terminalId: terminalId,
orderId: orderData.orderId,
amount: amountInKurus,
currency: '949',
cardNumber: orderData.cardNumber.replace(/\s/g, ''),
cardExpiry: orderData.cardExpiry, // Format: MMYY
cvv: orderData.cvv,
cardHolderName: orderData.cardHolderName,
hash: hash,
returnUrl: `https://${orderData.siteUrl}/_functions/halkbankCallback`,
cancelUrl: `https://${orderData.siteUrl}/payment-cancel`,
installment: orderData.installment || '0',
language: 'tr'
};
// Create header for Basic Authentication
const authHeader = Buffer.from(
`${apiUsername}:${apiPassword}`
).toString('base64');
// Send POST request to Halkbank API
const response = await fetch(
'https://sanalpos.halkbank.com.tr/api/v1/payment/3dpay',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${authHeader}`
},
body: JSON.stringify(requestData)
}
);
const result = await response.json();
if (result.status === 'success') {
return {
success: true,
redirectUrl: result.redirectUrl,
transactionId: result.transactionId
};
} else {
return {
success: false,
error: result.errorMessage || 'Payment initiation failed'
};
}
} catch (error) {
console.error('Payment initiation error:', error);
return {
success: false,
error: 'System error occurred'
};
}
}
This function starts with the export keyword because it must be callable from frontend code⁹. Inside the function, all sensitive information is first retrieved from Secrets Manager, then order data is formatted and sent to Halkbank. An important detail is that amounts are sent in kuruş - for 100 TL you should send 10000.
Step 4: Frontend Integration
Now that your backend functions are ready, it's time to prepare the interface that the customer will see. Open your payment page in Wix Editor and edit the page code in Dev Mode.
// Page Code
import { initiatePayment } from 'backend/halkbankService';
import wixWindow from 'wix-window';
$w.onReady(function () {
$w('#paymentButton').onClick(async () => {
// Disable button (double-click prevention)
$w('#paymentButton').disable();
$w('#loadingIcon').show();
try {
// Collect form data
const orderData = {
orderId: generateOrderId(),
amount: parseFloat($w('#totalAmount').text),
cardNumber: $w('#cardNumberInput').value,
cardExpiry: $w('#expiryInput').value,
cvv: $w('#cvvInput').value,
cardHolderName: $w('#cardHolderInput').value,
installment: $w('#installmentDropdown').value,
siteUrl: wixWindow.getSiteUrl()
};
// Form validation
if (!validatePaymentForm(orderData)) {
$w('#errorMessage').text = 'Please fill in all fields correctly';
$w('#errorMessage').show();
return;
}
// Call backend function
const result = await initiatePayment(orderData);
if (result.success) {
// Redirect to 3D Secure screen
wixWindow.openLightbox('ProcessingLightbox');
setTimeout(() => {
wixWindow.location.href = result.redirectUrl;
}, 1000);
} else {
$w('#errorMessage').text = result.error;
$w('#errorMessage').show();
}
} catch (error) {
console.error('Payment error:', error);
$w('#errorMessage').text = 'An error occurred. Please try again.';
$w('#errorMessage').show();
} finally {
$w('#paymentButton').enable();
$w('#loadingIcon').hide();
}
});
});
function generateOrderId() {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 10000);
return `ORD${timestamp}${random}`;
}
function validatePaymentForm(data) {
// Card number check (16 digits)
const cardNumber = data.cardNumber.replace(/\s/g, '');
if (!/^\d{16}$/.test(cardNumber)) return false;
// Expiry date check (MMYY format)
if (!/^\d{4}$/.test(data.cardExpiry)) return false;
// CVV check (3 digits)
if (!/^\d{3}$/.test(data.cvv)) return false;
// Cardholder name check
if (!data.cardHolderName || data.cardHolderName.length < 3) return false;
return true;
}
This code collects form data when the payment button is clicked, validates it, and calls the backend function. If successful, it redirects the customer to Halkbank's 3D Secure screen. A loading icon is shown for user experience and double-clicking the button is prevented.
Step 5: Callback Endpoint with HTTP Functions
After the 3D Secure verification is completed, Halkbank redirects the customer to the callback URL you specified. This URL must be an HTTP function because it needs to accept POST requests from outside. In Wix, HTTP functions are defined in the http-functions.js file¹⁰.
If the http-functions.js file doesn't exist in the "Backend" folder in Code Panel, create it. This file is special and automatically recognized by Wix¹⁰.
// backend/http-functions.js
import { ok, badRequest, notFound } from 'wix-http-functions';
import { getSecret } from 'wix-secrets-backend';
import wixData from 'wix-data';
import crypto from 'crypto';
export async function post_halkbankCallback(request) {
try {
// Get form data from POST body
const body = await request.body.text();
const params = parseFormData(body);
// Incoming parameters
const {
orderId,
status,
hash: receivedHash,
amount,
currency,
authCode,
transactionId,
errorCode,
errorMessage,
mdStatus
} = params;
// Hash verification
const expectedHash = await calculateCallbackHash(
orderId,
amount,
currency,
status
);
if (receivedHash !== expectedHash) {
console.error('Hash mismatch!', {
received: receivedHash,
expected: expectedHash
});
return badRequest({
body: 'Invalid hash'
});
}
// mdStatus check (3D Secure result)
// 1 = Successful, 2-7 = Failed
if (mdStatus !== '1') {
await updateOrderStatus(orderId, 'failed', {
errorCode,
errorMessage
});
return ok({
body: JSON.stringify({
redirect: `/payment-failed?orderId=${orderId}`
}),
headers: {
'Content-Type': 'application/json'
}
});
}
// Status check (Halkbank payment result)
if (status === 'APPROVED' || status === 'SUCCESS') {
// Update order
await updateOrderStatus(orderId, 'paid', {
authCode,
transactionId,
paymentDate: new Date()
});
// Redirect to success page
return ok({
body: JSON.stringify({
redirect: `/order-confirmation?orderId=${orderId}`
}),
headers: {
'Content-Type': 'application/json'
}
});
} else {
// Payment failed
await updateOrderStatus(orderId, 'failed', {
errorCode,
errorMessage
});
return ok({
body: JSON.stringify({
redirect: `/payment-failed?orderId=${orderId}`
}),
headers: {
'Content-Type': 'application/json'
}
});
}
} catch (error) {
console.error('Callback error:', error);
return badRequest({
body: 'Callback processing failed'
});
}
}
async function calculateCallbackHash(orderId, amount, currency, status) {
const merchantId = await getSecret('halkbank_merchant_id');
const securityKey = await getSecret('halkbank_security_key');
const dataString = `${merchantId}|${orderId}|${amount}|${currency}|${status}|${securityKey}`;
return crypto
.createHash('sha512')
.update(dataString, 'utf8')
.digest('base64');
}
async function updateOrderStatus(orderId, status, details) {
try {
// Update Orders collection using Wix Data
const query = wixData.query('Orders')
.eq('orderId', orderId);
const results = await query.find();
if (results.items.length > 0) {
const order = results.items[0];
await wixData.update('Orders', {
_id: order._id,
paymentStatus: status,
authorizationCode: details.authCode,
transactionId: details.transactionId,
paymentDate: details.paymentDate,
errorCode: details.errorCode,
errorMessage: details.errorMessage,
updatedAt: new Date()
});
return true;
}
return false;
} catch (error) {
console.error('Order update error:', error);
return false;
}
}
function parseFormData(body) {
const params = {};
const pairs = body.split('&');
for (const pair of pairs) {
const [key, value] = pair.split('=');
params[key] = decodeURIComponent(value);
}
return params;
}
Function names in the HTTP functions file are important. The post_halkbankCallback function captures POST requests to the https://yoursite.wixsite.com/functions/halkbankCallback URL¹⁰. The function name must start with post and include the URL path name.
Step 6: 3D Secure Verification Flow
The 3D Secure flow is the most critical stage where customer security is ensured. The flow works as follows:
When the customer enters card information and clicks the "Pay" button, the initiatePayment function is called and a request is sent to Halkbank API. The bank returns a redirectUrl in response - this URL is the address of the bank's 3D Secure verification screen. Your frontend code redirects the customer to this URL.
The customer enters the SMS password sent to their mobile phone on the bank screen. If the password is correct, the bank redirects the customer to the returnUrl you specified with a POST request. This request carries parameters containing the transaction result. Your HTTP function receives these parameters, verifies the hash, and updates the order status.
Meaning of mdStatus Codes
The mdStatus parameter indicates the result of 3D Secure verification¹¹:
1: Verification successful, transaction can proceed
2: Cardholder or issuing bank is not registered in the system
3: Issuing bank is not registered in the system
4: Verification attempt made but failed
5: Verification could not be performed
6: 3D Secure error
7: System error
Payment processing should only continue when mdStatus = 1. In all other cases, the transaction should be cancelled and an appropriate error message should be shown to the customer.
Step 7: Transaction Result Codes and Error Management
The status parameter returned from Halkbank indicates the payment transaction result. Possible values:
APPROVED / SUCCESS: Payment successful
DECLINED: Payment declined
ERROR: System error
Additionally, errorCode and errorMessage parameters provide detailed information for failed transactions. You should translate these codes into understandable Turkish messages when showing them to customers:
function getErrorMessage(errorCode) {
const messages = {
'01': 'Transaction cannot be made with your card. Please contact your bank.',
'05': 'Transaction declined. Please check your card information.',
'12': 'Invalid transaction. Please check your information.',
'51': 'Insufficient balance. Please try another card.',
'54': 'Your card has expired.',
'57': 'You do not have authorization for this transaction.',
'61': 'Limit exceeded. Transaction amount exceeds your card limit.',
'65': 'Your daily transaction limit has been exceeded.',
'75': 'Password attempt limit exceeded.'
};
return messages[errorCode] || 'Transaction failed. Please try again.';
}
These messages are used in your callback function to provide correct information to the customer.
Step 8: Order Management and Wix Data Integration
When payment transactions are completed, the order status needs to be updated. There are two methods for this in Wix: if you're using Wix Stores, you can use the built-in Order API, or if you're using a custom system, you can use Wix Data collections¹².
Order Recording with Wix Data
First, create an "Orders" collection in Wix Editor. Click "Add Collection" from Database Manager and add these fields:
orderId (Text) - Unique customerId (Text) customerEmail (Text) amount (Number) currency (Text) paymentStatus (Text) authorizationCode (Text) transactionId (Text) cardLastFour (Text) installment (Number) paymentDate (Date) errorCode (Text) errorMessage (Text) createdAt (Date) updatedAt (Date)
When an order is created, add a record to this collection:
import wixData from 'wix-data';
async function createOrder(orderData) {
try {
const order = {
orderId: orderData.orderId,
customerId: orderData.customerId,
customerEmail: orderData.customerEmail,
amount: orderData.amount,
currency: '949',
paymentStatus: 'pending',
cardLastFour: orderData.cardNumber.slice(-4),
installment: orderData.installment || 0,
createdAt: new Date(),
updatedAt: new Date()
};
const result = await wixData.insert('Orders', order);
return result;
} catch (error) {
console.error('Order creation error:', error);
throw error;
}
}
In the callback stage, the order is updated with the updateOrderStatus function.
Step 9: Adding Installment Support
The installment feature allows customers to split payments. Installment rates at Halkbank vary according to your agreement. You can offer installment options in the frontend with a dropdown:
// Fill installment options when page loads
$w.onReady(async function () {
const amount = parseFloat($w('#totalAmount').text);
const installments = await getInstallmentOptions(amount);
$w('#installmentDropdown').options = installments.map(opt => ({
label: opt.label,
value: opt.value.toString()
}));
});
// Calculate installment options in backend
export async function getInstallmentOptions(amount) {
// Minimum installment amount 100 TL
if (amount < 100) {
return [{ label: 'Single Payment', value: '0' }];
}
const options = [
{ installment: 0, rate: 0, label: 'Single Payment' },
{ installment: 2, rate: 0, label: '2 Installments (Commission-free)' },
{ installment: 3, rate: 1.5, label: '3 Installments (+1.5%)' },
{ installment: 6, rate: 3, label: '6 Installments (+3%)' },
{ installment: 9, rate: 4.5, label: '9 Installments (+4.5%)' },
{ installment: 12, rate: 6, label: '12 Installments (+6%)' }
];
return options.map(opt => {
const totalAmount = amount * (1 + opt.rate / 100);
const monthlyPayment = opt.installment > 0
? (totalAmount / opt.installment).toFixed(2)
: totalAmount.toFixed(2);
const label = opt.installment === 0
? `Single Payment (${amount.toFixed(2)} TL)`
: `${opt.installment} Installments (Monthly ${monthlyPayment} TL)`;
return {
label: label,
value: opt.installment
};
});
}
When an installment is selected, the number of installments is sent as a parameter to the initiatePayment function and communicated to Halkbank API.
Step 10: Testing and Going Live
After the setup is completed, testing the system is critically important. If Halkbank doesn't provide a test environment, you should test with real small-amount transactions.
Test Checklist
Hash Calculation: Check calculated hashes with console.log
API Connection: Verify that requests are being sent to Halkbank API
3D Redirect: Test that customer is redirected to bank screen
Callback Processing: Check that HTTP function is working and receiving parameters
Hash Verification: Test that hash verification works in callback
Order Update: Check that Orders collection is being updated correctly
Error Scenarios: Test situations like incorrect card information, insufficient balance
Installments: Verify that different installment options work
Steps to Go Live
After the test phase is successful:
Publish the site by clicking the "Publish" button in Wix Editor
Switch from test mode to live mode in Halkbank panel (if available)
Make the first real transaction yourself for a final check
Notify your Halkbank customer representative that you've gone live
Monitor transactions closely for the first few days
If there are any issues, examine the logs
Secure and Fast Payment Infrastructure
When the correct steps are followed, Halkbank POS setup on Wix can be completed completely securely and quickly. In the first step, API credentials are securely added to Velo backend, then payment endpoints are configured and callback URLs are prepared. A verification flow is established for 3D Secure redirects to work correctly, where bank return parameters, hash verification, and transaction result codes are checked.
After the customer confirms the transaction on Halkbank's verification screen, the system processes this data and completes the order. If the setup steps are followed correctly, your Wix store will accept both installment and single payment transactions smoothly, and the payment flow will have a completely secure structure. Remember that maintaining this system is also important - regularly check logs, follow hash algorithm updates, and pay attention to notifications from Halkbank.
References
Halkbank. "Virtual POS API Documentation". Internal resource.
Elfsight. (2025). "How to Add API to Wix Website: Step-by-Step Integration Guide". Access: https://elfsight.com/tutorials/how-to-add-api-to-wix-website/
Qukasoft. "Halkbank Integration". Access: https://help.qukasoft.com/odeme-yontemleri/halkbank-entegrasyonu
serB2B. "Halkbank Virtual POS Integration". Access: https://docs.serenay.net.tr/en/settings-advanced/odeme-ayarlari/sanal-pos-ayarlari/settings-basic-halkbank/
Akıllı Ticaret. (2025). "Halkbank Virtual Pos Integration". Access: https://akilliticaret.com/konu/halkbank-sanal-pos-entegrasyonu
Wix. "Course: Coding with Velo: Backend". Access: https://www.wix.com/learn/courses/coding/velo-backend
Medium - CodeX. (2021). "Using Velo by Wix to Integrate 3rd-Party API Data". Access: https://medium.com/codex/using-velo-by-wix-to-integrate-3rd-party-api-data-e9e121a638e7
Webimonline. "Halkbank Virtual POS Hash Error Support". Access: https://www.webimonline.com/halkbank-sanal-pos-hash-hatasi-destek
Wix Velo. "API Overview". Access: https://dev.wix.com/docs/velo
Davydov Consulting. "Wix API Integration | Wix Velo API". Access: https://www.davydovconsulting.com/velocode/api-integration-into-wix-website
GitHub - mewebstudio. "pos: Virtual pos package for Turkish banks". Access: https://github.com/mewebstudio/pos
Wix Velo. "Wix Pay Backend Introduction". Access: https://dev.wix.com/docs/velo/apis/wix-pay-backend/introduction

Blakfy Expert
