How to Create a Custom Payment Form with Velo

The project

I’m a Wix Legacy Partner and have been working with Wix for over 6 years. I am also a member of the 1972 graduating class from Orange Highschool in Ohio. Next year will be the 50th Year Class Reunion for my fellow graduates. I volunteered to create a website that allowed the, now 66 – 68-year-old, classmates to register for specific activities. I also created a donation function to help defer the cost of the rather elaborate weekend.

As with all sites I build, this site was built on the Wix platform. Some of the functions used Wix supplied apps (notably the Events app). Some functions used basic Velo coding and data collections, but one function, donations, was built from scratch using the Wix Payment API.

Donation Form

Wix Pay is the built-in system to collect payments on Wix and, thanks to its API, I was able to create, with a small amount of code, a donation form that allows the user to decide the amount they wish to donate.

Upon completing and submitting the form, Velo creates a payment that prompts a payment pop-up. If the payment is validated, the submission is recorded and the user is automatically added to the sponsor's list.

How I Made It

First, I created the payment form with input elements to collect the user’s first name, last name, email, and the amount they wish to donate. I then connected the form’s input to a dataset (a representation of a database entry) and bound the submit button to the save operation. . Finally I connected the success and error messages to text elements. And that is it for the UI!

The Code (Frontend)

Before accepting a donation I needed to create and then trigger the payment. The process is therefore started by the `onBeforeSave` hook on the dataset. The hook creates the payment via a backend function `createDonationPayment` that it receives the form value as a JSON. The payment is then used to trigger the payment via the wixPay API (frontend). If the payment is successful, the save operation will proceed otherwise the operation is canceled


import wixPay from 'wix-pay'; import { createDonationPayment } from "backend/donation.jsw"; $w.onReady(function () { const $donationDataset = $w("#donationDataset"); $donationDataset.onBeforeSave(() => triggerPayment($donationDataset.getCurrentItem())); }); /** * @param {import("public/types").DonationData} donationData */ async function triggerPayment(donationData) { const $errorText = $w("#errorText"); const isDonationCompleted = donationData.firstName && donationData.lastName && && donationData.donationAmount if(!isDonationCompleted) { $errorText.text = "Form is not completed" return false; } const payment = await createDonationPayment(donationData).catch(reason => { console.error("payment could not be initialized", reason); return {id: null} }) const result = await wixPay.startPayment( => { console.error("payment failed", reason); $errorText.text = "Payment Failed. Contact the administrator"; }); if(!result) return false; if(result.status === "Cancelled") { $errorText.text = ""; return false; } else if(result.status !== "Successful") { console.warn("payment is not successful", result) $errorText.text = "Payment Failed. Contact the administrator"; return false; } else { return true; } }


The donation is processed by Wix Payments, the contact information is written to a data collection and then refreshed to a Sponsors page where the donor’s name and email address are added to a repeater display.

One of the reasons the donation information is recorded in a data collection is to make it available to download as a CSV file, for other record-keeping purposes.

Finally, an autoresponder is used to send a Thankyou Letter to the donor using dynamic values inserted from the data collection.

The Code (backend)

Here I used a web module (Wix internal backend endpoint) to generate a payment based on the information collected in the form.

import wixPayBackend from 'wix-pay-backend';

/** * @param {import("public/types").DonationData} donationData */ export function createDonationPayment(donationData) { if(!donationData) throw new Error('missing donation data') const amount = donationData.donationAmount; if(!amount || amount < 1) throw new Error("invalid amount(" + amount + ")"); /** @type wix_pay_backend.PaymentUserInfo */ const userInfo = { firstName: donationData.firstName, lastName: donationData.lastName, email:, countryCode: 'USA', phone: null, } /** @type wix_pay_backend.PaymentInfo */ const paymentInfo = { items: [ { name: "Donation to OHS'", price: amount } ], amount: amount, userInfo: userInfo, }; return wixPayBackend.createPayment(paymentInfo); }

Ascend as a Non-coding Solution

While all this could have been accomplished using Wix Forms, the reunion committee did not want to pay for Ascend (premium CRM from Wix) for no other reason than to collect donations. They felt that between the gateway fees and the additional cost of the website for a Business Premium Plan (allowing them a payment gateway) they were paying enough fees. This system accomplishes all that they need while keeping the user’s journey simple. This ease of use is important as the demographic of the users (66-68 years old) suggests they may be less than computer savvy.

Security Issues & Trade-Offs

Because of the limited scale of the project I didn't need to secure the functionality more than necessary instead choosing to keep everything as simple as possible.

The main issue with the current architecture is that the Donations collection (database table) is open to “write” by any user. This means a hacker could insert a donation record directly into our database bypassing the donation form and the payment transaction. Even though this scenario is possible, it is unlikely to happen, won’t harm the project overall, and could easily be recovered based on the transaction history.

Should you have any questions about this project, please contact me at

361 views0 comments