auth_bypass
handleConfirmTransfer is hooked to init and completes a transfer from a GET link (?wlps_action=confirm_transfer&transfer_id=&token=). validateTransferToken only checks that the row is still pending; there is no SELECT … FOR UPDATE, no compare-and-swap on status, and status is not flipped to a terminal value until after performPointTransfer returns. Two parallel requests from the logged-in sender both observe pending, both pass validateSenderAndRecipient (both see the pre-debit balance), and both call performPointTransfer. That method always credits the full transfer amount: the credit addExtraPointAction runs even if debit returned false, and WPLoyalty's Base::addExtraPointAction (required parent plugin) clamps an oversized debit to the remaining balance instead of aborting, so a second debit of 0 still returns success while the credit of N points proceeds. A customer with 1 point can therefore issue many concurrent confirmation requests and grow the recipient WPLoyalty balance without a matching debit. Those points are store credit (WPLoyalty redeem-to-coupon). The 2/minute rate limit applies only to transfer initiation, not confirmation.
App/Controller/PointTransferController.php:144-202
RecommendationConsume the pending row atomically before moving points (UPDATE ... SET status='processing' WHERE id=%d AND status='pending' AND token=%s and require affected_rows=1). Run debit and credit in a single DB transaction and abort the credit if debit does not remove the full amount. Do not credit when debit returns false or when the parent helper clamped the debit to 0. Redirect to a token-free URL after handling the GET.