a
    %ßiB"  ã                   @   s¨   d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	 ddl
Z
ddlmZ eedœdd„ZG d	d
„ d
ƒZeeeeeeef dœdd„Zeeeef dœdd„ZdS )z]
Purchase Validation and Anti-Cheat

Handles rate limiting, idempotency, and IAP validation.
é    N)Úwraps)ÚDictÚAnyÚOptional)Úget_database_connection)Ú	max_callsÚtime_windowc                    s   i ‰ ‡ ‡‡fdd„}|S )a   
    Rate limiting decorator.

    Args:
        max_calls: Maximum calls allowed
        time_window: Time window in seconds

    Usage:
        @rate_limit(max_calls=10, time_window=60)
        def some_function(player_id: int, ...):
            ...
    c                    s$   t ˆ ƒtdœ‡‡ ‡‡fdd„ƒ}|S )N)Ú	player_idc                    sŽ   t   ¡ ‰ | ˆv r.‡ ‡fdd„ˆ|  D ƒˆ| < ng ˆ| < tˆ|  ƒˆkrjt d| › dˆj› �¡ ddddœS ˆ|   ˆ ¡ ˆ| g|¢R i |¤ŽS )	Nc                    s   g | ]}ˆ | ˆk r|‘qS © r
   )Ú.0Út)Únowr   r
   ú8/var/www/lichun.app/lichun/ws/monetization/validation.pyÚ
<listcomp>&   ó    zBrate_limit.<locals>.decorator.<locals>.wrapper.<locals>.<listcomp>zRate limit exceeded for player z on Fz-Too many requests. Please wait and try again.ZRATE_LIMIT_EXCEEDED©ÚsuccessÚmessageÚ
error_code)ÚtimeÚlenÚloggingÚwarningÚ__name__Úappend)r	   ÚargsÚkwargs)ÚcallsÚfuncr   r   )r   r   Úwrapper    s    ýz.rate_limit.<locals>.decorator.<locals>.wrapper)r   Úint)r   r   ©r   r   r   )r   r   Ú	decorator   s    zrate_limit.<locals>.decoratorr
   )r   r   r"   r
   r!   r   Ú
rate_limit   s    r#   c                   @   sX   e Zd ZdZeeeedœdd„ƒZeeedœdd„ƒZ	eeee
eef dœd	d
„ƒZdS )ÚIdempotencyManagerzN
    Prevent double-processing of transactions (especially IAP receipts).
    )r	   Útransaction_idÚreturnc                 C   s   t  | › d|› � ¡ ¡ ¡ S )zá
        Generate idempotency key from transaction.

        Args:
            player_id: The player's ID
            transaction_id: Unique transaction ID

        Returns:
            SHA256 hash as idempotency key
        ú:)ÚhashlibÚsha256ÚencodeÚ	hexdigest)r	   r%   r
   r
   r   Úgenerate_keyB   s    zIdempotencyManager.generate_key)Úidempotency_keyr&   c              
   C   sæ   d}d}z¾zPt ƒ }|jdd�}| d| f¡ | ¡ }|d dkW W |rN| ¡  |rZ| ¡  S  tyª } z8t d|› �¡ W Y d}~W |r’| ¡  |rž| ¡  dS d}~0 0 W |rº| ¡  |râ| ¡  n|rÔ| ¡  |rà| ¡  0 dS )	zÂ
        Check if transaction already processed.

        Args:
            idempotency_key: The idempotency key

        Returns:
            True if already processed, False otherwise
        NT)Ú
dictionaryzOSELECT COUNT(*) as count FROM processed_transactions WHERE idempotency_key = %sÚcountr   z Error checking idempotency key: F)r   ÚcursorÚexecuteÚfetchoneÚcloseÚ	Exceptionr   Úerror)r-   Úconnr0   ÚresultÚer
   r
   r   Úcheck_processedP   s@    þöú
ýz"IdempotencyManager.check_processed)r-   r	   Útransaction_datac              
   C   s¸   d}d}z�z2t ƒ }| ¡ }| d| |t |¡f¡ | ¡  W n@ ty| } z(|rX| ¡  t 	d|› �¡ W Y d}~n
d}~0 0 W |rŒ| 
¡  |r´| 
¡  n|r¦| 
¡  |r²| 
¡  0 dS )zÏ
        Mark transaction as processed.

        Args:
            idempotency_key: The idempotency key
            player_id: The player's ID
            transaction_data: Transaction data to store
        Nz‹INSERT INTO processed_transactions
                   (idempotency_key, player_id, transaction_data)
                   VALUES (%s, %s, %s)z(Error marking transaction as processed: )r   r0   r1   ÚjsonÚdumpsÚcommitr4   Úrollbackr   r5   r3   )r-   r	   r:   r6   r0   r8   r
   r
   r   Úmark_processedt   s,    
ü(
ýz!IdempotencyManager.mark_processedN)r   Ú
__module__Ú__qualname__Ú__doc__Ústaticmethodr    Ústrr,   Úboolr9   r   r   r?   r
   r
   r
   r   r$   =   s   #r$   )r	   Úreceipt_datar%   Ú
product_idr&   c           
   
   C   s@  t  | |¡}t  |¡r2t d|› �¡ ddddœS dddd	d
œ}z®ddi}|d dkrÄt  || ||ddœ¡ | |d¡}|dkr´ddlm} || d|› �|ƒ d|d|› d�dœW S ddddœW S n(t 	d| › d|d › �¡ ddddœW S W nL t
�y: }	 z2tj	d| › d|	› �dd� ddd dœW  Y d!}	~	S d!}	~	0 0 d!S )"a,  
    Validate IAP purchase with Apple.

    Args:
        player_id: The player's ID
        receipt_data: Base64 encoded receipt from Apple
        transaction_id: Unique transaction ID
        product_id: Product identifier

    Returns:
        dict with 'success' and 'diamonds_awarded' keys
    z%Duplicate IAP transaction attempted: FzTransaction already processedZDUPLICATE_TRANSACTIONr   éd   i,  iî  iÐ  )zcom.baolife.diamonds.smallzcom.baolife.diamonds.mediumzcom.baolife.diamonds.largezcom.baolife.diamonds.megaÚstatusr   r   )r%   rG   Zvalidation_statusé   )Úaward_diamondsZiap_purchase_TzSuccessfully purchased z	 diamonds)r   Údiamonds_awardedr   zInvalid product IDZINVALID_PRODUCTz!IAP validation failed for player z	: status zPurchase validation failedZVALIDATION_FAILEDz Error validating IAP for player z: )Úexc_infozServer error during validationÚSERVER_ERRORN)r$   r,   r9   r   r   r?   ÚgetÚdiamond_economyrK   r5   r4   )
r	   rF   r%   rG   Zidem_keyZdiamond_packagesZvalidation_resultZdiamond_amountrK   r8   r
   r
   r   Úvalidate_iap_receiptš   sV    
ýü	ý
ýý
ýýrQ   )r	   Úmessage_datac           	      C   s¼   |  d¡}|  d¡}|  d¡}t|||gƒsB|| ddddœƒ dS t| |||ƒ}|d	 rš|| d
d|d |d dœƒ ddlm} || ƒ}|| d|dœƒ n|| d|  dd¡|d dœƒ dS )zë
    WebSocket handler for IAP validation.

    Args:
        player_id: The player's ID
        message_data: Message containing receipt_data, transaction_id, product_id
        send_to_client: Function to send response to client
    rF   r%   rG   r5   ÚINVALID_REQUESTz*Missing required fields for IAP validation)Útyper   r   Nr   ZpurchaseValidatedTrL   r   )rT   r   rL   r   rJ   )Úget_diamond_balanceZdiamondUpdate)rT   Zbalancer   ÚPURCHASE_FAILED)rO   ÚallrQ   rP   rU   )	r	   rR   Úsend_to_clientrF   r%   rG   r7   rU   Únew_balancer
   r
   r   Úhandle_validate_purchaseò   s:    	


ýüþ

ýrZ   )rB   r(   r   r;   Ú	functoolsr   Útypingr   r   r   r   Údatabaser   r    r#   r$   rD   rQ   rZ   r
   r
   r
   r   Ú<module>   s   -]ÿX