|
46 | 46 | StripePaymentService, |
47 | 47 | SUPPORTED_CURRENCIES, |
48 | 48 | ) |
49 | | -from payments.x402 import X402PaymentService, get_x402_service |
50 | 49 |
|
51 | 50 | """ |
52 | 51 | Crypto Wallet Authentication Extension |
@@ -328,13 +327,6 @@ def __init__(self, **kwargs): |
328 | 327 | self.crypto_service = CryptoPaymentService(price_service=self.price_service) |
329 | 328 | self.stripe_service = StripePaymentService(price_service=self.price_service) |
330 | 329 |
|
331 | | - # Initialize x402 payment service (if configured) |
332 | | - try: |
333 | | - self.x402_service = get_x402_service() |
334 | | - except ValueError: |
335 | | - # X402_MERCHANT_WALLET not configured, x402 disabled |
336 | | - self.x402_service = None |
337 | | - |
338 | 330 | @self.router.get( |
339 | 331 | "/v1/wallet/providers", |
340 | 332 | summary="Get supported wallet providers", |
@@ -637,180 +629,9 @@ async def verify_crypto_invoice( |
637 | 629 | return PaymentTransactionResponse(**record) |
638 | 630 |
|
639 | 631 | # ---------------------------- |
640 | | - # x402 Payment Protocol endpoints |
| 632 | + # Stripe Payment endpoints |
641 | 633 | # ---------------------------- |
642 | 634 |
|
643 | | - @self.router.get( |
644 | | - "/v1/billing/x402/enabled", |
645 | | - tags=["Billing", "x402"], |
646 | | - ) |
647 | | - async def check_x402_enabled(): |
648 | | - """Check if x402 payment protocol is enabled""" |
649 | | - return { |
650 | | - "enabled": self.x402_service is not None, |
651 | | - "facilitator": ( |
652 | | - self.x402_service.facilitator_url if self.x402_service else None |
653 | | - ), |
654 | | - "network": self.x402_service.network if self.x402_service else None, |
655 | | - } |
656 | | - |
657 | | - @self.router.post( |
658 | | - "/v1/billing/x402/payment-request", |
659 | | - tags=["Billing", "x402"], |
660 | | - ) |
661 | | - async def create_x402_payment_request( |
662 | | - seat_count: int, |
663 | | - currency: str = "USDC", |
664 | | - user=Depends(verify_api_key), |
665 | | - ): |
666 | | - """ |
667 | | - Create an x402 payment request for API resource access. |
668 | | - This returns the payment details that would go in a 402 response. |
669 | | - """ |
670 | | - if not self.x402_service: |
671 | | - raise HTTPException( |
672 | | - status_code=503, detail="x402 payment protocol not configured" |
673 | | - ) |
674 | | - |
675 | | - user_id = self._get_user_id(user) |
676 | | - if not user_id: |
677 | | - raise HTTPException(status_code=401, detail="User context missing") |
678 | | - |
679 | | - # Get price quote |
680 | | - quote = await self.price_service.get_quote(currency, seat_count) |
681 | | - |
682 | | - # Get AGiXT URI from environment |
683 | | - agixt_uri = getenv("AGIXT_URI") |
684 | | - |
685 | | - # Create x402 payment request |
686 | | - payment_request = self.x402_service.create_payment_request( |
687 | | - amount=quote["amount_currency"], |
688 | | - currency=currency, |
689 | | - description=f"AGiXT API Access - {seat_count} seat(s)", |
690 | | - resource=f"{agixt_uri}/v1/", |
691 | | - ) |
692 | | - |
693 | | - return payment_request |
694 | | - |
695 | | - @self.router.post( |
696 | | - "/v1/billing/x402/verify", |
697 | | - response_model=PaymentTransactionResponse, |
698 | | - tags=["Billing", "x402"], |
699 | | - ) |
700 | | - async def verify_x402_payment( |
701 | | - request: Request, |
702 | | - seat_count: int, |
703 | | - currency: str = "USDC", |
704 | | - user=Depends(verify_api_key), |
705 | | - ): |
706 | | - """ |
707 | | - Verify and process an x402 payment from the X-PAYMENT header. |
708 | | - This endpoint handles the payment verification and settlement. |
709 | | - """ |
710 | | - if not self.x402_service: |
711 | | - raise HTTPException( |
712 | | - status_code=503, detail="x402 payment protocol not configured" |
713 | | - ) |
714 | | - |
715 | | - user_id = self._get_user_id(user) |
716 | | - if not user_id: |
717 | | - raise HTTPException(status_code=401, detail="User context missing") |
718 | | - |
719 | | - # Get payment payload from X-PAYMENT header |
720 | | - payment_payload = request.headers.get("X-PAYMENT") |
721 | | - if not payment_payload: |
722 | | - raise HTTPException(status_code=400, detail="Missing X-PAYMENT header") |
723 | | - |
724 | | - # Get expected amount and recreate payment requirements |
725 | | - quote = await self.price_service.get_quote(currency, seat_count) |
726 | | - expected_amount = quote["amount_currency"] |
727 | | - |
728 | | - # Get AGiXT URI from environment |
729 | | - agixt_uri = getenv("AGIXT_URI") |
730 | | - |
731 | | - # Recreate the payment request (includes paymentRequirements) |
732 | | - payment_request = self.x402_service.create_payment_request( |
733 | | - amount=expected_amount, |
734 | | - currency=currency, |
735 | | - description=f"AGiXT API Access - {seat_count} seat(s)", |
736 | | - resource=f"{agixt_uri}/v1/", |
737 | | - ) |
738 | | - |
739 | | - # Extract the x402 payment requirements for facilitator |
740 | | - payment_requirements = payment_request.get("paymentRequirements") |
741 | | - |
742 | | - try: |
743 | | - # Process payment (verify + settle + record) |
744 | | - payment_record = await self.x402_service.process_payment( |
745 | | - payment_payload=payment_payload, |
746 | | - amount=expected_amount, |
747 | | - currency=currency, |
748 | | - user_id=user_id, |
749 | | - description=f"AGiXT API Access - {seat_count} seat(s)", |
750 | | - payment_requirements=payment_requirements, |
751 | | - seat_count=seat_count, |
752 | | - ) |
753 | | - |
754 | | - # Return payment record and set X-PAYMENT-RESPONSE header |
755 | | - response_data = PaymentTransactionResponse( |
756 | | - reference_code=payment_record.reference_code, |
757 | | - status=payment_record.status, |
758 | | - currency=payment_record.currency, |
759 | | - amount_usd=payment_record.amount_usd, |
760 | | - amount_currency=payment_record.amount_currency, |
761 | | - exchange_rate=payment_record.exchange_rate, |
762 | | - seat_count=payment_record.seat_count, |
763 | | - transaction_hash=payment_record.transaction_hash, |
764 | | - wallet_address=payment_record.wallet_address, |
765 | | - memo=payment_record.memo, |
766 | | - metadata=( |
767 | | - json.loads(payment_record.metadata_json) |
768 | | - if payment_record.metadata_json |
769 | | - else None |
770 | | - ), |
771 | | - expires_at=payment_record.expires_at, |
772 | | - created_at=payment_record.created_at, |
773 | | - ) |
774 | | - |
775 | | - # Add X-PAYMENT-RESPONSE header with transaction details |
776 | | - from fastapi.responses import JSONResponse |
777 | | - |
778 | | - # Parse metadata from JSON string |
779 | | - metadata = ( |
780 | | - json.loads(payment_record.metadata_json) |
781 | | - if payment_record.metadata_json |
782 | | - else {} |
783 | | - ) |
784 | | - payer = metadata.get("payer", "") |
785 | | - blockchain_tx = payment_record.transaction_hash |
786 | | - |
787 | | - # Convert Pydantic model to dict with JSON-safe datetime serialization |
788 | | - response = JSONResponse( |
789 | | - content=json.loads(response_data.model_dump_json()) |
790 | | - ) |
791 | | - |
792 | | - # Create X-PAYMENT-RESPONSE header following x402 spec |
793 | | - # Base64 encode the settlement response |
794 | | - import base64 |
795 | | - |
796 | | - settlement_response = { |
797 | | - "success": True, |
798 | | - "payer": payer, |
799 | | - "transaction": blockchain_tx, |
800 | | - "network": self.x402_service.network, |
801 | | - } |
802 | | - response.headers["X-PAYMENT-RESPONSE"] = base64.b64encode( |
803 | | - json.dumps(settlement_response).encode() |
804 | | - ).decode() |
805 | | - |
806 | | - return response |
807 | | - |
808 | | - except Exception as e: |
809 | | - logging.error(f"x402 payment verification failed: {str(e)}") |
810 | | - raise HTTPException( |
811 | | - status_code=400, detail=f"Payment verification failed: {str(e)}" |
812 | | - ) |
813 | | - |
814 | 635 | @self.router.post( |
815 | 636 | "/v1/billing/stripe/payment-intent", |
816 | 637 | response_model=StripePaymentIntentResponse, |
|
0 commit comments