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}zzPt  }|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}zz2t  }| }|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
        NzINSERT 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
}zddi}|d dkrt || ||dd ||d}|dkrd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