Hasta ahora, todo bien, esto al menos evitará que el usuario realice el pago en varias sesiones (varias veces tratando de pagar la misma tarjeta; es bueno lidiar con los dobles clics).
¿Cómo lo compruebas? Con un SELECT
estándar o con SELECT ... FOR UPDATE
? Según el paso 5, supongo que está revisando una columna reservada en el elemento o algo similar.
El problema aquí es que el SELECT ... FOR UPDATE
en el paso 2 NO se va a aplicar el FOR UPDATE
bloqueo a todo lo demás. Solo se aplica a lo que es SELECT
ed:el cart-item
mesa. Según el nombre, ese será un registro diferente para cada carrito/usuario. Esto significa que NO se bloqueará el proceso de otras transacciones.
Siguiendo lo anterior, según la información que ha proporcionado, puede terminar con varias personas comprando el mismo artículo, si no está usando SELECT ... FOR UPDATE
en el paso 3.
Solución sugerida
- Comenzar transacción
SELECT ... FOR UPDATE
elcart-item
mesa.
Esto bloqueará la ejecución de un doble clic. Lo que seleccione aquí debe ser algún tipo de columna de "carrito ordenado". Si hace esto, una segunda transacción se detendrá aquí y esperará a que termine la primera, y luego leerá el resultado que guardó la primera en la base de datos.
Asegúrese de finalizar el proceso de pago aquí si el cart-item
la tabla dice que ya se ha pedido.
SELECT ... FOR UPDATE
la tabla donde registras si un artículo ha sido reservado.
Esto impedirá que OTROS carritos/usuarios puedan leer esos elementos.
Según el resultado, si los artículos no están reservados, continúe:
-
UPDATE ...
la tabla en el paso 3, marcando el artículo como reservado. Haz cualquier otroINSERT
s yUPDATE
s que necesita, también. -
Hacer el pago. Emita una reversión si el servicio de pago dice que el pago no funcionó.
-
Registre el pago, si tiene éxito.
-
Confirmar transacción
Asegúrese de no hacer nada que pueda fallar entre los pasos 5 y 7 (como enviar correos electrónicos), de lo contrario, puede terminar haciendo un pago sin que se registre, en caso de que la transacción se revierta.
El paso 3 es el paso importante con respecto a asegurarse de que dos (o más) personas no intenten pedir el mismo artículo. Si dos personas lo intentan, la segunda persona terminará con su página web "colgada" mientras procesa la primera. Luego, cuando finalice el primero, el segundo leerá la columna "reservado" y podrá devolver un mensaje al usuario de que alguien ya compró ese artículo.
Pago en transacción o no
Esto es subjetivo. Por lo general, desea cerrar las transacciones lo más rápido posible, para evitar que varias personas no puedan interactuar con la base de datos a la vez.
Sin embargo, en este caso, realmente quieres que esperen. Es sólo una cuestión de cuánto tiempo.
Si elige confirmar la transacción antes del pago, deberá registrar su progreso en alguna tabla intermedia, ejecutar el pago y luego registrar el resultado. Tenga en cuenta que si el pago falla, deberá deshacer manualmente los registros de reserva de artículos que actualizó.
SELECCIONE... PARA ACTUALIZAR en filas inexistentes
Solo una palabra de advertencia, en caso de que el diseño de su tabla implique insertar filas donde necesita SELECT ... FOR UPDATE
antes :Si una fila no existe, esa transacción NO hará que otras transacciones esperen, si también SELECT ... FOR UPDATE
la misma fila inexistente.
Por lo tanto, asegúrese de serializar siempre sus solicitudes haciendo SELECT ... FOR UPDATE
en una fila que sabes que existe primero. Luego puede SELECT ... FOR UPDATE
en la fila que puede o no existir todavía. (No intente hacer solo SELECT
en la fila que puede existir o no, ya que leerá el estado de la fila en el momento en que comenzó la transacción, no en el momento en que ejecuta SELECT
. Entonces, SELECT ... FOR UPDATE
en filas inexistentes todavía es algo que debe hacer para obtener la información más actualizada, solo tenga en cuenta que no hará que otras transacciones esperen).