
     i0                     
   d dl m Z mZ d dlmZ d dlmZ d dlZd dlZd dlZd dl	m
Z
mZmZmZmZ d dlmZmZ dZej                            d          Zd	d
dZdefdZdedee         fdZdefdZdededz  dedz  fdZdS )    )datetime	timedelta)current_app)and_N)dbSubscriptionSubscriptionStatusPaymentMethodPaymentStatus)PaymentHistoryr   z"https://pay.toss.im/api/v2/refundsTOSS_API_KEYu   반품 취소(전체 환불)reasonc          
      b   t          | j        pd          }|dk    rdddddS dt          j                    j        dd          }t          |dz            }||z
  }t
          | j        |||d|d|d	}	 t          j        t          d	d
i|d          }|j
        dk    rt          j        | _        d| | _        t          j                    | _        t$          j                                         t*          j                            d| j         d|            d||ddS t*          j                            d|j
         d|j                    dd|d|j
         d|j         dS # t6          $ r=}t*          j                            d           dd|t;          |          dcY d}~S d}~ww xY w)u   
    단일 PaymentHistory에 대해 환불 API 호출 + DB 상태 갱신.
    - latest_payment: PaymentHistory (COMPLETED)
    return: dict(success: bool, amount: int, refund_no: str|None, err: str|None)
    r   TN)successamount	refund_noerrrefund_   g?)	apiKeypayTokenrefundNor   amountTaxableamountTaxFree	amountVatamountServiceFeer   zContent-Typezapplication/json   )headersjsontimeout   u   환불 완료 - u3   [_refund_single_payment] 환불 성공: payment_id=z, refund_no=u/   [_refund_single_payment] 환불 실패: status=z, body=FzHTTP z: u-   [_refund_single_payment] 환불 호출 예외)intr   uuiduuid4hexr   payment_keyrequestspostTOSS_REFUND_URLstatus_codePHStatusREFUNDEDstatusdescriptionr   now
updated_atr   sessioncommitr   loggerinfoiderrortext	Exception	exceptionstr)	latest_paymentr   refund_amountr   amount_taxable
amount_vatpayloadres	            S/home/kdj-ubuntu1/mlink_AI_Server/mlink-backend/src/services/cancel_orchestrator.py_refund_single_paymentrD      s'    -233M14MMM1$*,,*3B3/11I,--N/J ".'
 
GVM/NDV3W^eoqrrr=C$,$5N!)GI)G)GN&(0N%J##  %EZhZk  %E  %E  zC  %E  %E  F  F  F#}9]abbb$$%uWXWd%u%umnms%u%uvvv$	RsZ[ZgRsRsklkqRsRsttt V V V$$%TUUU AIcRSffUUUUUUUUVs&   5B+E' !AE' '
F.12F)#F.)F.subscription_idc                     t           j                            | t          j                                      t           j                                                  }|                                S )u   
    환불 대상 결제건 수집 (기간 연장형: 최근 결제부터 처리하는 것이 일반적이나,
    여기선 전 결제 체인 환불을 목표로 COMPLETED 상태 전부를 타겟팅)
    )rE   r.   )	r   query	filter_byr,   	COMPLETEDorder_by
created_atdescall)rE   qs     rC   _collect_target_paymentsrO   C   sN    
 
	
)OH<N)
O
O
(>,1133
4
4  5577N    subscriptionrefunded_paymentsc                 r   d}|D ],}	 |t          |j        pd          z  }# t          $ r Y )w xY w|dk    r| j        t	          |          z
  | _        t          j                    | _        t          j	        
                                 t          j                            d| d| j                    dS dS )u   
    모든 환불이 성공한 뒤, 각 결제건이 부여했던 일수(subscription_days)만큼
    종료일을 되돌립니다. (기간 연장형 모델 회수)
    r   )daysu;   [_revert_subscription_period_after_refunds] 회수 일수: u   일, new_end_date=N)r#   subscription_daysr9   end_dater   r   r0   r1   r   r2   r3   r   r4   r5   )rQ   rR   total_days_recoverphs       rC   )_revert_subscription_period_after_refundsrY   N   s    
   	#b&:&?a"@"@@ 	 	 	D	 A , 5	GY8Z8Z8Z Z"*,..

 !H^p !H !H0<0E!H !H 	I 	I 	I 	I 	I	 s   "
//c                 v   t          j                                                    }| j        r| j                                        n|}||k    rt          j        | _        d| _        nt          j        | _        d| _        t          j                    | _        t          j
                                         	 ddlm}m} ddlm} |j                            | j                  }|r|j        |j        k    r || j        dt-          | j                  d          }|                    d          r8t0          j                            d	|                    d
d           d           n5t0          j                            d|                    d                      nF# t8          $ r9}t0          j                            dt-          |                      Y d}~nd}~ww xY w	 ddlm} |j                            | j                  }|rLd|_        t          j
                                         t0          j                            d|j                    n9# t8          $ r,}t0          j                            d|            Y d}~nd}~ww xY wt0          j                            d| j                    dS )u`   
    구독 최종 취소 처리 + 사용자 권한 회수 + 크레딧 사용 완료 처리
    Nr   )UserSubscriptionType)consume_all_remaining_creditsu6   구독 취소로 인한 남은 크레딧 사용 완료subscription_cancellation)user_idr/   reference_idreference_typer   u9   [_finalize_cancellation] 남은 크레딧 사용 완료: consumedu
    크레딧u9   [_finalize_cancellation] 남은 크레딧 사용 실패: r7   uA   [_finalize_cancellation] 크레딧 사용 처리 실패(무시): )r[   	role_freeu:   [_finalize_cancellation] 사용자 권한 회수: user_id=u7   [_finalize_cancellation] 역할 회수 실패(무시): u6   [_finalize_cancellation] 구독 취소 완료: sub_id=)r   r0   daterV   r	   	CANCELLEDr.   next_billing_dater1   r   r2   r3   src.models.userr[   r\   src.utils.credit_managerr]   rG   getr_   subscription_typeCREDITr;   r6   r   r4   r5   warningr9   role)	rQ   todayend_date_onlyr[   r\   r]   usercredit_resultrB   s	            rC   _finalize_cancellationrr   b   s?   
 LNN!!E4@4ITL)..000uM0:)-&& 1:)-&&lnnLJq::::::::JJJJJJz~~l233 
	ED*.>.EEE99$,T 11:	  M   ++ E"''  )Qdqdudu  wA  CD  eE  eE  )Q  )Q  )Q  R  R  R  R"**  ,Dgtgxgx  zA  hB  hB  ,D  ,D  E  E  E q q q""#ogjklgmgm#o#oppppppppqb((((((z~~l233 	l#DIJ##$jaeah$j$jkkk b b b""#`]^#`#`aaaaaaaab fUaUdffgggggs2   6C'F 
G!(/GG!%A3I 
J#"J

Jorder_id	pay_tokenr   c                 "	    d t          |pd          ddddg ddd}	 t                     o                     d          pd v }|s$t          j                            d             |S t          j                             	          	                    t          j
                                                                                  }|s$t          j                            d
             |S |j        t          j        k    rnt          j        |_        t#          j                    |_        t(          j                                         t          j                            d|j                    t2          j                            |j        t6          j                                                  }|s&t          j                            d           d|d<   |S t;          |j                  }|s:t          j                            d           d|d<   t=          |           d|d<   |S dt          dt          fd fd|D             }d}	g }
|D ]O} |          r0t          j                            d|j         d|j                    >tA          |d          }|j        |!                    dd          |!                    dd          |!                    d          |!                    d          d}|d         d         "                    |           |!                    d          rW|d         d xx         d!z  cc<   |d         d"xx         |!                    dd          z  cc<   |
"                    |           7|d         d#xx         d!z  cc<   d}	Q|	rItG          ||
           t=          |           d|d<   d|d<   t          j                            d$           nBt(          j        $                                 t          j                            d%           d|d<   |S # tJ          $ rI}t          j        &                    d&           t(          j        $                                 |cY d'}~S d'}~ww xY w)(u  
    (정산 결제 성공 후 호출)
    1) 정산 PENDING PH → COMPLETED 보장
    2) 현재 활성 구독의 결제 체인 전체 환불 시도 (COMPLETED들 전부 환불)
    3) 모든 환불 성공 시 구독 상태 CANCELLED + 권한 회수
    4) 일부 환불 실패 시: 구독은 유지(취소 미완료)하고, 실패 내역을 리턴/로그

    return: dict {
      success: bool,
      settlement: {order_id, amount},
      refund: {
        total_refund: int,
        success_count: int,
        fail_count: int,
        items: [{payment_id, refund_amount, success, refund_no, error}]
      },
      cancelled: bool
    }
    Fr   )rs   r   )total_refundsuccess_count
fail_countitems)r   
settlementrefund	cancelledmlink_usage__usage_u9   [finish_settlement_then_refund] 정산 주문이 아님: rs   z:[finish_settlement_then_refund] PH not found for order_id=uB   [finish_settlement_then_refund] 정산 PH COMPLETED 보정: ph_id=)r_   r.   uo   [finish_settlement_then_refund] ACTIVE 구독 없음 — 취소할 대상이 없어 환불만 발생한 상태.Tr   u;   [finish_settlement_then_refund] 환불 대상 결제 없음r|   rX   returnc                     | j         pd}| j        pd}|                    d          p"| j        pd                                dk    pd|v S )N r}   onetimeu   정산)rs   product_name
startswithbilling_cyclelower)rX   oidnames      rC   _is_usage_settlementz;finish_settlement_then_refund.<locals>._is_usage_settlement   s^    ;$"CO)rD ~.. $$*1133y@$t#rP   c                 D    g | ]} |          sr|j         k    |S  r   ).0rX   r   rs   s     rC   
<listcomp>z1finish_settlement_then_refund.<locals>.<listcomp>   sO     
 
 
''++
 
 #%+"9"9 "9"9"9rP   z,[FinishSettle] skip usage settlement PH: id=z, order=u#   전체 환불(정산 선결제 후)r   r   r   r   )
payment_idr=   r   r   r7   r{   ry   rw      rv   rx   uM   [finish_settlement_then_refund] 전체 환불 성공 → 구독 취소 완료uj   [finish_settlement_then_refund] 일부 환불 실패 — 구독 취소 보류(유지). 운영介入 필요u-   [finish_settlement_then_refund] 예외 발생N)'r#   boolr   r   r4   rl   r   rG   rH   rJ   rK   rL   firstr7   r.   r,   rI   r   r0   r1   r   r2   r3   r5   r6   r   r_   r	   ACTIVErO   rr   rs   rD   ri   appendrY   rollbackr9   r:   )rs   rt   r   resultis_settlementrX   rQ   target_paymentscompleted_listall_refund_okrefundable_completedprritemrB   r   s   `              @rC   finish_settlement_then_refundr      s   * #+s6;Q7G7GHH#$qTVWW	 FcXiH,?,?,O,O,hS\`hSh 	&&'mck'm'mnnnM !++X+>>GGHaHfHfHhHhiiooqq 	$$%lbj%l%lmmmM9*** *BI$LNNBMJ##$pikin$p$pqqq $)33BJOaOh3iiooqq 	&&  (Y  Z  Z  Z $F9M 3<?CC 	##$abbb $F9"<000"&F;M	^ 	 	 	 	 	
 
 
 
 
(
 
 
 !  	& 	&A##A&& "''(qWXW[(q(qefeo(q(qrrr'2WXXXBd!#!!4!466)U33VVK00 D 8W%,,T222vvi   &x 111Q6111x 000BFF8Q4G4GG000$++A....x ...!3... %  	&5lDXYYY"<000"&F; $F9##$stttt J!!!$$  &R  S  S  S %F9   $$%TUUU

s:   AP; 0BP; 7C,P; $AP; 4HP; ;
R>R	R	R)r   r   flaskr   
sqlalchemyr   osr$   r(   src.models.subscriptionr   r   r	   r
   r   src.models.paymentr   r,   r*   environri   r   rD   r#   rO   listrY   rr   r;   r   r   rP   rC   <module>r      s   ( ( ( ( ( ( ( (             				   f f f f f f f f f f f f f f H H H H H H H H 7z~~n-- 6T ,V ,V ,V ,V ,V^c    IL I]abp]q I I I I(2h 2h 2h 2h 2hj~s ~sTz ~SVY]S] ~ ~ ~ ~ ~ ~rP   