Can you do multiple refunds / rebates to one realex transaction? - realex-payments-api

I currently have an issue whereby I am processing single purchases that contain multiple items as one transaction in realex. But when I come to refund say 2 of 4 of these items once I have a refunded the first item and I try and refund the second item I get an error from Realex saying that the transaction has already been refunded.
This is a more high level question as to whether or not it is even possible to do multiple refunds to one Realex transaction.

Thanks for your question. It is possible to process multiple refunds (or rebates as we refer to them) against a single transaction. This isn't setup by default on your account, however. You can email your account manager and request that multiple rebates be enabled.
Best,
Seán
Channel Support
Realex Payments

Related

Structure and logic for payouts in a marketplace application

I'm developing a marketplace-style application that allows users to upload purchasable digital items -> the public purchases these items -> and for my application to pay the users (owners of items) their owed funds via PayPal Payouts API on a daily basis.
I'm struggling with how best to calculate/store the owing balance, and how to map the individual purchase transaction records to the concept of a "payout" (when we send owed funds to the user).
Schema so far:
User
id
name
createdAt
etc.
Purchasable Item
id
user_id (owner)
price
createdAt
etc.
Transaction
id
type ("purchase" or "payout")
status (depending on PayPal response. COMPLETED, FAILED, REFUNDED etc.)
value (integer (lowest demomination of currency). Positive integer for purchase, negative for a payout).
purchasable_id (For "purchase" transactions, reference the ID of the purchasable item that was purchased)
transaction_fee
createdAt
payout_id (?) The ID of the payout (below) this purchase is included in. Not sure about this. This won't be known at the time of the transaction, so it would need to be updated to store it and I'm not sure how to know which transaction will belong in which payout?
Payout
Not sure about this. Feels like a duplicate of a payout transaction entry, but I want a way to store which purchase transactions were paid out in which payouts.
id
status (depending on PayPal response to Payout API webhook. COMPLETED, FAILED, REFUNDED etc.)
createdAt
Logic:
This is where I need the most help.
CRON job. Every 24hrs:
Calculate each users balance by summing the payout_balance_change fields of the Transactions table. i.e balance isn't stored, it's always calculated. Is that a good idea?
Insert a row into "Transactions" of type "payout" with a negative "payout_balance_change". i.e. subtracting the amount we will send in the payout, zeroing their balance in the Transactions table.
Insert a row into "Payouts" table that stores the details of the payout attempt.
Problems:
How will I know which purchase transactions belong to each payout cycle (so I can then store the payout_id in those transaction records). I could use the date of the transaction, and each payout could be for the 24hr period prior to the CRON job? I'm flexible on this and not sure what the most robust logic would be.
Any advice on how best to structure this, or links to similar projects would be greatly appreciated.
Thank you!
and welcome to Stack Overflow.
This question may be a too wide for this format - please do read "how to ask".
Firstly - I'm answering on the assumption this is MySQL. Again - please read "how to ask", tagging with multiple technologies isn't helpful.
Fistly - this on how to store money in MySQL.
Secondly - the common pattern for doing this is to have the transaction table only reflect complete transactions. That way, the current balance is always sum(transaction_value), with the transaction date showing you the balance at a given point in time. You typically store the interim state for each transaction in a dedicated table (e.g. "payout"), and only insert into the transaction table once that payout transaction is complete.
You should remove all the status and transaction_fee references from the transaction table, and store them in the dedicated tables. A transaction_fee can be represented as a transaction in its own right.
If you want to store the relationship between purchase and payout, you might have something like:
Payout
Payout_id
Payout_amount
Payout_status
Payout_date
...
Purchase
Purchase_id
Customer_id
Item_id
Purchase_date
....
Payout_purchase
Purchase_id
Payout_id
Your logic then becomes:
cron job searches all purchases that haven't been paid out (where purchase_id not in (select purchase_id from payout_purchase)
for each vendor:
create new record in payout_purchase
find sum of new payout_purchase records
attempt payout
if (payout succeeded)
insert record into transaction table with vendor ID, payout ID and payout amount
else
handle error case. This could be deleting the record (and logging the failure somewhere else), or by adding a "status" column with the value "failed". The latter option makes it easier to provide your vendors with a statement - "we attempted to pay you, but the payment failed". Either way, you want to have a way of monitoring failures, and monitor them.
end if
next vendor
I've left out the various state and error management logic steps.
Things you want to worry about:
What happens if a purchase occurs while the payout logic is running? You need to make sure you work on defined data sets in each step. For instance, you need to insert data into the "payout_purchase" table, and then work only on those records - new purchases should not be included until the next run.
What happens if a payout fails? You must ensure they are included in the next payment run.
How do you provide a statement to your buyers and sellers? What level of detail do you want?
Transaction management from MySQL may help, but you need to spend time learning the semantics and edge cases.

Forming my ERD, couple of issues with it

Okay, my partners and myself created these a while ago. We are going to be transferring this into SQL through Visual Basic soon, but I want to make sure everything is ready to go. Two major complaints that we were not able to fix was...
"By having Transaction and Product directly connected you are unable to allow multiple products on the same order (customer can't order both a latte and cappuccino on the same order)."
"Membership table: Not sure what the data in DiscountTypeTotal means - do you have multiple pieces of data in the same field? (then your table isn't on 1NF).
It looks like you need to allow each member to have multiple discounts - so you need another table to capture that."
How do we effectively correct these? How else would we connect Transaction with Products? I understand that the customer can only purchase one item per transactions, so would we have products and another table for multiple items? Allowing multiple customers to have discounts, I am lost. Any help would be appreciated.

What's the correct way to protect against multiple sessions getting the same data?

Let's say I have a table called tickets which has 4 rows, each representing a ticket to a show (in this scenario these are the last 4 tickets available to this show).
3 users are attempting a purchase simultaneously and each want to buy 2 tickets and all press their "purchase" button at the same time.
Is it enough to handle the assignment of each set of 2 via a TRANSACTION or do I need to explicitly call LOCK TABLE on each assignment to protect against the possibility that 2 of the tickets will be assigned to two users.
The desire is for one of them to get nothing and be told that the system was mistaken in thinking there were available tickets.
I'm confused by the documentation which says that the LOCK will be implicitly released when I start a TRANSACTION, and was hoping to get some clarity on the correct way to handle this.
If you use a transaction, MySQL takes care of locking automatically. That's the whole point of transactions -- they totally prevent any kind of interference due to overlapping requests.
You could use "optimistic locking": When updating the ticket as sold, make sure you include the condition that the ticket is still available. Then check if the update failed (you get a count of rows updated, can be 1 or 0).
For example, instead of
UPDATE tickets SET sold_to = ? WHERE id = ?
do
UPDATE tickets SET sold_to = ? WHERE id = ? AND sold_to IS NULL
This way, the database will assure that you don't get conflicting updates. No need for explict locking (the normal transaction isolation will be sufficient).
If you have two tickets, you still need to wrap the two calls into a single transaction (and roll back if either of them failed.

MySQL INSERT SELECT WHERE race condition

I'm working on a ticketing system where users escrow a large amount of tickets at once (basically all tickets that are not out of stock) before claiming them. These tickets that shown to the user and they can select whichever ticket they want to claim.
This escrow system could introduce race conditions if two users try to escrow the same tickets at the same time and there aren't enough tickets, as in:
Tickets left: 1
User A hits the page, checks number of tickets left. 1 ticket left
User B hits the page, checks number of tickets left. 1 ticket left
Since they both have a ticket left they would both escrow the ticket, making tickets left -1.
I'd like to avoid locking if at all possible and am wondering if a statement with subqueries like
INSERT INTO ticket_escrows (`ticket`,`count`)
SELECT ticket,tickets_per_escrow FROM tickets WHERE tickets.total > (
COALESCE(
SELECT SUM(ticket_escrows.count) FROM ticket_escrows
WHERE ticket_escrows.ticket = tickets.id
AND ticket_escrows.valid = 1
,0)
+
COALESCE(
SELECT SUM(ticket_claims.count)
FROM ticket_claims
WHERE ticket_claims.ticket = tickets.id
,0)
)
will be atomic and allow me to prevent race conditions without locking.
Specifically I'm wondering if the above query will prevent the following from happening:
Max tickets: 50 Claimed/Escrowed tickets: 49
T1: start tx -> sums ticket escrows --> 40
T2: start tx -> sums ticket escrows --> 40
T1: sums ticket claims --> 9
T2: sums ticket claims --> 9
T1: Inserts new escrow since there is 1 ticket left --> 0 tickets left
T2: Inserts new escrow since there is 1 ticket left --> -1 tickets left
I'm using InnoDB.
To answer your question "if a statement with subqueries ... will be atomic": In your case, yes.
It will be atomic only when enclosed in a transaction. Since you state you're using InnoDB, the query even with subqueries is an SQL statement and as such executed in a transaction. Quoting the documentation:
In InnoDB, all user activity occurs inside a transaction. If autocommit mode is enabled, each SQL statement forms a single transaction on its own.
...If a statement returns an error, the commit or rollback behavior depends on the error.
Also, isolations levels matter.
In terms of the SQL:1992 transaction isolation levels, the default InnoDB level is REPEATABLE READ
REPEATABLE READ may not be enough for you, depending on the logic of your program. It prevents transactions from writing data that was read by another transaction until the reading transaction completes, phantom reads are possible, however. Check SET TRANSACTION for a way how to change the isolation level.
To answer your second question "if the above query will prevent the following from happening ...": In a transaction with the SERIALIZABLE isolation level it cannot happen. I believe the default level should be safe as well in your case (supposing tickets.total doesn't change), but I'd prefer having it confirmed by someone.
You have really left a lot of information out about how you want this to work, which is why you haven't gotten more/better answers.
Ticketing is a matter of trade-offs. If you show someone there are 10 tickets available, either you immediately make all 10 tickets unavailable for everyone else (which is bad for everyone else) or your don't, which means that person could potentially order a ticket that someone else snapped up while they were deciding which ticket to take. An "escrow" system doesn't really help matters, as it just moves the problem from which tickets to purchase to which tickets to escrow.
During the period where you are not locking everyone else out, the best practice is to craft your SQL in such a way that updates or inserts will fail if someone else has modified the data while you were working on it. This can be as simple as incrementing a counter in the row every time the row is changed and using that counter (plus the primary key) in the WHERE clause of the UPDATE statement. If the counter changed, then the update fails and you know you've lost the race.
I don't understand what you want to have happen or your data structures enough to give you much more advice.

What should your transaction management strategy be for an e-commerce system?

What is the general pattern or approach to managing transactions in a web-based e-commerce system? How do you handle the situation where more than one user tries to buy the last item, for example?
To prevent two users from purchasing the same stock item of which there is only 1 unit in stock, you need to check that each item in a user's cart has stock available right before you create an order and decrement stock for that item.
This operation will have to be atomic and only one order can be processed at any given time (read: database transaction), which should not be a problem if you are using a central database for stock management.
If stock has run out by the time a client checks out, you should remove the item from the client's cart and redirect them to their shopping cart, informing them of the situation.
Of course, this situation only occurs when two users both add the same stock item to their cart of which only one unit is in stock and one of them checks out. First come, first served. You should generally not allow clients to add products to their cart if no stock is available at that moment, unless you can order new stock within a reasonable amount of time, but in that case, the whole point is moot.
You can take a preemptive approach by checking that stock is available the moment a client initiates checkout and take the same route as above. However, that would depend on the nature of your product and the volume of transactions vs cancelled orders. If it is likely that another order for the same item was cancelled in the meantime and stock becomes available by the time a client checks out, then you don't want to lose a sale by telling the client that no stock is available. Better to let the order fail at the moment stock is not available and inform the client of the situation, which is rare after all.
Why not take the order and then get that item for the customer, perhaps a bit later? You can win a repeat customer :)