Easy Receipt
Payment proof, without the screenshots.
A customer uploads their bank transfer receipt on the order confirmation page, against the exact order, with your account details on a card they can tap to copy. Then Approve and Reject arrive in your Telegram. A WooCommerce plugin for every store where card-to-card transfer is how people pay.
Built by Pigment Dev, where I am a co-founder and lead developer. It is GPL and free, on its way to WordPress.org, with a paid edition to follow.
On the order confirmation page
In the reviewers' Telegram chat
رسید تازه برای یک سفارش
اقلام، مبلغ کل، روش پرداخت، مشتری و نشانی، از قالب پیام خودتان
رد
دلیل را بنویسید تا برای مشتری فرستاده شود.
فقط بازبینهای مجاز میتوانند این دکمهها را بزنند. هر تصمیم در یادداشتهای سفارش ثبت میشود.
Back on the order page, after a rejection
What that flow does
- The card number, the IBAN and the amount copy themselves when tapped
- The drop zone becomes a preview, so the customer knows what they sent
- Approve and Reject arrive in Telegram, and a rejection carries its reason to the customer
- A complete Persian translation, laid out right to left, with every string editable
// why
Why we built it
In a lot of the world, "pay by card" does not mean a checkout form. It means a transfer from one card to another, and a photo of the confirmation.
Before
On most of the stores we looked after, a card-to-card order went like this. The customer reached the confirmation page and found a paragraph of text: a card number, maybe an account holder's name, an instruction to send proof. They opened their banking app, typed the number by hand, made the transfer and took a screenshot.
Then the screenshot went somewhere. WhatsApp, usually. Sometimes Telegram, email, or a form on another page. It arrived with no order number, because nobody had told the customer to include one. The owner opened it, squinted at the amount, found the matching order in the list and marked it paid. For every single order.
The proof and the order were never in the same place, so a person had to be the join.
WooCommerce knew the order, the bank knew the transfer, and the customer had the receipt. What was missing was a way to attach the receipt to the order at the moment it was made, on the page the customer was already looking at, and then approve it from wherever the owner actually was, which is rarely the WordPress admin.
So the whole flow sits on the confirmation page, and review moves to Telegram. It grew quickly once it existed, because every store we put it on asked for the next thing: more than one receipt per order, a re-upload after a rejection, their bank's own card design, their own wording, an SMS as well as an email. Version 1.0 shipped in June 2026, and all of that was in by 1.19.
// how-it-works
How it works
Three steps. The customer does the first, you do the second from wherever you are, and the order does the third on its own.
The customer uploads on the order page
On the order-received page, in My Account, in a block or through a shortcode. They see the amount and your account card, tap to copy the number, make the transfer and drop in the receipt. The upload is tied to the order by its own key, so it cannot land on the wrong one.
You review it from the admin, email or Telegram
The receipt shows on the order and in a status column on the orders list. It also arrives in Telegram with Approve and Reject. Only the people you have allow-listed can act, and every action becomes an order note.
The order moves on
Approval moves the order to the status you chose. Rejection moves it to another, and the customer gets an email, and optionally an SMS, with the reason and a fresh upload form.
A three-step wizard runs on activation: which payment methods show the widget, your destination account, done. It can be run again from the settings header.
// the-widget
The widget
Built without jQuery, it inherits the theme's fonts and behaves like part of a current store rather than a form from a decade ago.
One box that becomes the preview
Drag a file in or tap to choose, and the drop zone turns into a large preview, portrait-friendly so a phone photo of a receipt is legible. Any receipt opens full size in a lightbox.
Two layouts
Minimal: a compact, centred card that goes amount, account, upload, submit. Or Detailed, with the payment details beside the upload.
Shown wherever the order is
The classic order-received page, the block-based confirmation, the My Account order view, a shortcode and a block, at the position you choose on the page.
Your content around it
Rich text before and after the widget, each with its own switch, and custom CSS that loads only where the widget appears.
More than one receipt
One receipt by default, or as many as you allow, in numbered slots, each reviewed on its own. Call them receipts, cheques or proofs.
Re-upload after rejection
The customer sees the rejected image, the reason and a fresh upload form. Approving the replacement moves the order on.
Status you can see
A badge in the widget header, a banner while payment is pending, and a gallery of uploaded receipts, each with its date and status.
Every word is yours
A Translate tab where every customer-facing string is an editable field with macros for the order number, total and email, and the original shown so it can always be restored.
// the-card
The destination card
The account the customer pays into is drawn as a real card, in the bank's colours, that copies itself when tapped. Most of the design work went here.
- Many accounts
- One or many destination accounts, each row collapsing to a summary line.
- A bank picker
- A searchable, illustrated grid that matches Latin and Persian names.
- Banks, with their logos
- Iranian banks and neobanks, Turkish banks, and PayPal, Stripe and ZarinPal. Or your own name and logo.
- Clean digits
- Numbers typed in Persian or Arabic digits are stored clean, so a copy always yields plain digits.
- The bank's own design
- Colours taken from the bank's logo, with the logo placed and sized for that bank.
- Or yours
- Two patterns recoloured per account, or a blank panel when plain is right.
- A visual editor
- Select the number, IBAN, holder or amount on a live card, then move and resize it.
- A typeface per element
- The theme's font, a custom one or an Elementor global typography. A Persian face is bundled.
- A hint that it copies
- A shine sweeps across the copyable fields when the card first scrolls into view.
// review
Review anywhere
Every channel goes through the same transition. Approving in Telegram and approving in the admin do exactly the same thing: update the receipt, write the note, change the status, send the emails, fire the hooks.
In the admin
A meta box on the order with each receipt, newest first, and Approve, Reject and a reason. A status column on the orders list with counts and thumbnails, on both the new and the legacy order screens.
In Telegram
One message per receipt from your own template, with Approve and Reject under it. Reject asks for a reason in a short conversation. Reviewers are allow-listed by Telegram user ID, behind a secret-protected webhook registered on save.
Per receipt, per order
Each file has its own status. The order is pending while any file is pending, approved only when all are, and rejected when any is.
Status changes
Pick what the order becomes when a receipt is awaited, uploaded, approved or rejected.
Six emails, three designs
Customer and admin, for uploaded, approved and rejected. Each in the WooCommerce style, a Pigment design or your own HTML, with a live preview and the receipt attached to admin emails.
SMS as well
Six independent switches and a template per event, through four providers: WP SMS, Persian WooSMS, FarazSMS or IPPanel, and our own toolkit's gateway.
// privacy
Private by design
A receipt is a picture of someone's bank activity. It is treated like one.
Never a public URL
Files are streamed through a login-gated viewer. Only the order's owner and the reviewers you allow can see them.
Checked against the order key
The upload endpoint validates the order key before it accepts a file, so nobody can attach a receipt to an order they do not own.
Your file rules
Allowed file types and a maximum size are yours to set.
Nothing sent elsewhere
No data goes to third parties. The only outbound connection is to the Telegram Bot API, and only when you switch Telegram on with your own bot.
// developers
For developers
Built the way we would want a plugin we inherited to be built.
One source of truth
One transition owns receipt state. The front end, the admin and Telegram all go through it: it saves the meta, writes the note, changes the status, records history and fires the events.
26 hooks, documented
React to uploads, approvals and rejections per order and per file. Restrict file types, change the receipt limit, add banks and card designs.
Current WooCommerce
HPOS-compatible and compatible with the cart and checkout blocks. Namespaced, autoloaded, with coding standards enforced by a lint step.
Translation
A translation template and a complete Persian translation ship with it, and customer-facing strings can also be changed from the admin.
// Per file, and for the whole order easy_receipt_item_uploaded $order_id, $attachment_id, $context easy_receipt_item_approved $order_id, $attachment_id, $context easy_receipt_item_rejected $order_id, $attachment_id, $context easy_receipt_status_changed $order_id, $new_status, $old_status, $context // Change the defaults, add banks and card designs easy_receipt_default_settings easy_receipt_bank_card_designs
// products/
Other things I keep running
Plugins, apps and internal systems. Each one is shipped, documented, and still looked after.
- Ultimate InvoiceInvoices, slips and labels for WooCommerce
- Ultimate Integration for TelegramEvery site event, in the chat you already read
- Easy ReceiptBank transfer receipts, against the right order
- Advanced Profile SolutionLogin, registration, profiles and the account area
- Q&A CommentsA moderated Q&A your experts actually answer
- ToolkitEvery fix we kept writing twice, written once
- html2elementorA finished design, rebuilt as a real Elementor site
- UptimeA WordPress fleet, watched from one channel
- Client HubA client's whole account, inside Telegram
- TunnelGuardDomain-level VPN split tunneling on macOS