# Validar compra de Paddle

> Valida una compra utilizando el token de Paddle proporcionado, usando las credenciales de Paddle en la configuración de tu app dentro del Adapty Dashboard.
> Si la compra es válida, el historial de transacciones se importa desde Paddle al perfil en Adapty con el customer_user_id especificado.
> Si no existía ningún perfil con ese customer_user_id, se creará uno nuevo.

## OpenAPI

```yaml
/api-specs/adapty-api.yaml post /api/v2/server-side-api/purchase/paddle/token/validate/
openapi: 3.1.0
info:
  title: API de servidor de Adapty
  version: 1.0.0
servers:
  - url: https://api.adapty.io
    description: Servidor de producción
paths:
  /api/v2/server-side-api/purchase/paddle/token/validate/:
    post:
      summary: Validar compra de Paddle
      description: |
        Valida una compra utilizando el token de Paddle proporcionado, usando las credenciales de Paddle en la configuración de tu app dentro del Adapty Dashboard.
        Si la compra es válida, el historial de transacciones se importa desde Paddle al perfil en Adapty con el customer_user_id especificado.
        Si no existía ningún perfil con ese customer_user_id, se creará uno nuevo.
      operationId: validatePaddlePurchase
      tags:
        - Paddle
      security:
        - apikeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaddleValidationRequest"
            example:
              customer_user_id: <YOUR_CUSTOMER_USER_ID>
              paddle_token: live_7d279f61a3339fed520f7cd8c08
      responses:
        "200":
          description: Compra validada correctamente
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileResponse"
              example:
                data:
                  app_id: 14c3d623-2f3a-455a-aa86-ef83dff6913b
                  profile_id: 3286abd3-48b0-4e9c-a5f6-ac0a006804a6
                  customer_user_id: Jane.doe@example.com
                  total_revenue_usd: 0
                  segment_hash: 8f45947bad31ab0c
                  timestamp: 1736436751469
                  custom_attributes:
                    - key: favourite_sport
                      value: yoga
                  access_levels: []
                  subscriptions:
                    - purchase_id: 5a7ab471-2299-45f7-ad69-1d395c1256e3
                      store: app_store
                      store_product_id: 1year.premium
                      store_base_plan_id: null
                      store_transaction_id: "30002109551456"
                      store_original_transaction_id: "30002109551456"
                      purchased_at: "2022-10-12T09:42:50+00:00"
                      environment: Production
                      is_refund: false
                      is_consumable: false
                  non_subscriptions: []
        "400":
          description: Solicitud incorrecta
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                no_products_found:
                  summary: No se encontraron productos
                  value:
                    errors:
                      - No products found
                    error_code: no_products_found
                    status_code: 400
                paddle_api_key_not_found:
                  summary: Clave API de Paddle no encontrada
                  value:
                    errors:
                      - Paddle API key not found
                    error_code: paddle_api_key_not_found
                    status_code: 400
                invalid_paddle_credentials_or_purchase_not_found:
                  summary: Credenciales de Paddle no válidas o compra no encontrada
                  value:
                    errors:
                      - Invalid Paddle credentials or purchase not found
                    error_code: invalid_paddle_credentials_or_purchase_not_found
                    status_code: 400
        "401":
          description: No autorizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                errors:
                  - Invalid API key
                error_code: unauthorized
                status_code: 401
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
components:
  schemas:
    PaddleValidationRequest:
      type: object
      properties:
        customer_user_id:
          type: string
          description: El ID de tu usuario en tu sistema
        paddle_token:
          type: string
          description: |
            Token de un objeto de Paddle que representa una compra única.
            Puede ser un ID de transacción (txn_...) o un ID de suscripción (sub_...)
      required:
        - customer_user_id
        - paddle_token
    ProfileResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/Profile"
      required:
        - data
    ErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              source:
                type: string
                nullable: true
                description: Origen del error
              errors:
                type: array
                items:
                  type: string
                description: Array de mensajes de error
        error_code:
          type: string
          description: Nombre corto del error
        status_code:
          type: integer
          description: Código de estado HTTP
      required:
        - errors
        - error_code
        - status_code
    Profile:
      type: object
      properties:
        app_id:
          type: string
          format: uuid
          description: El ID interno de tu app
        profile_id:
          type: string
          format: uuid
          description: ID de perfil de Adapty
        customer_user_id:
          type: string
          nullable: true
          description: El ID de tu usuario en tu sistema
        total_revenue_usd:
          type: number
          format: float
          description: Un valor decimal que representa los ingresos totales en USD generados por el perfil
        segment_hash:
          type: string
          description: Parámetro interno
        timestamp:
          type: integer
          format: int64
          description: Hora de la respuesta en milisegundos; necesario para resolver una condición de carrera
        custom_attributes:
          type: array
          items:
            $ref: "#/components/schemas/CustomAttribute"
          description: Se permite un máximo de 30 atributos personalizados por perfil
        access_levels:
          type: array
          items:
            $ref: "#/components/schemas/AccessLevel"
          description: Array de objetos de nivel de acceso. Array vacío si el cliente no tiene niveles de acceso
        subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/Subscription"
          description: Array de objetos de suscripción. Array vacío si el cliente no tiene suscripciones
        non_subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/NonSubscription"
          description: Array de objetos de compras no suscritas. Array vacío si el cliente no tiene compras
      required:
        - app_id
        - profile_id
        - customer_user_id
        - total_revenue_usd
        - segment_hash
        - timestamp
        - custom_attributes
        - access_levels
        - subscriptions
        - non_subscriptions
    CustomAttribute:
      type: object
      properties:
        key:
          type: string
          maxLength: 30
          description: La clave debe ser una cadena de texto de no más de 30 caracteres. Solo se permiten letras, números, guiones, puntos y guiones bajos
        value:
          oneOf:
            - type: string
            - type: number
          description: El valor del atributo no debe superar los 50 caracteres. Solo se permiten cadenas de texto y números decimales como valores
      required:
        - key
        - value
    AccessLevel:
      type: object
      properties:
        access_level_id:
          type: string
          description: Identificador del nivel de acceso
        store:
          type: string
          description: Store donde se adquirió el nivel de acceso
        store_product_id:
          type: string
          description: ID del producto en el store
        store_base_plan_id:
          type: string
          nullable: true
          description: ID del plan base en el store
        store_transaction_id:
          type: string
          description: ID de la transacción en el store
        store_original_transaction_id:
          type: string
          description: ID de la transacción original en el store
        offer:
          allOf:
            - $ref: "#/components/schemas/OfferDTO"
          nullable: true
          description: Detalles de la oferta, si se aplicó una oferta promocional o introductoria
        starts_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo comienza el nivel de acceso
        purchased_at:
          type: string
          format: date-time
          description: Cuándo se adquirió el nivel de acceso
        originally_purchased_at:
          type: string
          format: date-time
          description: Cuándo se adquirió originalmente el nivel de acceso
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo expira el nivel de acceso
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo se canceló la renovación
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo se detectó el problema de facturación
        is_in_grace_period:
          type: boolean
          description: Si el nivel de acceso está en período de gracia
        cancellation_reason:
          type: string
          nullable: true
          description: Motivo de la cancelación
    Subscription:
      type: object
      properties:
        store:
          type: string
          description: Store donde se adquirió la suscripción
        store_product_id:
          type: string
          description: ID del producto en el store
        store_base_plan_id:
          type: string
          nullable: true
          description: ID del plan base en el store
        store_transaction_id:
          type: string
          description: ID de la transacción en el store
        store_original_transaction_id:
          type: string
          description: ID de la transacción original en el store
        offer:
          allOf:
            - $ref: "#/components/schemas/OfferDTO"
          nullable: true
          description: Detalles de la oferta, si se aplicó una oferta promocional o introductoria
        environment:
          type: string
          description: Entorno (Sandbox, Production)
        purchased_at:
          type: string
          format: date-time
          description: Cuándo se adquirió la suscripción
        originally_purchased_at:
          type: string
          format: date-time
          description: Cuándo se adquirió originalmente la suscripción
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo expira la suscripción
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo se canceló la renovación
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: Cuándo se detectó el problema de facturación
        is_in_grace_period:
          type: boolean
          description: Si la suscripción está en período de gracia
        cancellation_reason:
          type: string
          nullable: true
          description: Motivo de la cancelación
    NonSubscription:
      type: object
      properties:
        purchase_id:
          type: string
          format: uuid
          description: Identificador único de la compra
        store:
          type: string
          description: Store donde se realizó la compra
        store_product_id:
          type: string
          description: ID del producto en el store
        store_base_plan_id:
          type: string
          nullable: true
          description: ID del plan base en el store
        store_transaction_id:
          type: string
          description: ID de la transacción en el store
        store_original_transaction_id:
          type: string
          description: ID de la transacción original en el store
        purchased_at:
          type: string
          format: date-time
          description: Cuándo se realizó la compra
        environment:
          type: string
          description: Entorno (Sandbox, Production)
        is_refund:
          type: boolean
          description: Si se trata de un reembolso
        is_consumable:
          type: boolean
          description: Si se trata de una compra consumible
    OfferDTO:
      type: object
      properties:
        category:
          type: string
          enum:
            - introductory
            - promotional
            - offer_code
            - win_back
          description: Categoría de la oferta
        type:
          type: string
          enum:
            - free_trial
            - pay_as_you_go
            - pay_up_front
          description: Tipo de oferta
        id:
          type: string
          nullable: true
          description: ID de la oferta
      required:
        - category
        - type
  securitySchemes:
    apikeyAuth:
      type: apiKey
      name: Authorization
      in: header
      default: Api-Key {Your secret API key}
      description: |
        Las solicitudes a la API deben autenticarse con tu clave API secreta en la cabecera **Authorization**
        con el valor `Api-Key {your_secret_api_key}`, por ejemplo,
        `Api-Key secret_live_...`. Puedes encontrar esta clave en el Adapty Dashboard ->
        **App Settings** -> pestaña **General** -> sección **API keys**.
```
