# Validate Paddle purchase

> Validates a purchase using the provided Paddle token using the credentials of Paddle in your App Settings inside Adapty Dashboard. 
> If the purchase is valid, the transaction history is imported from Paddle to the profile in Adapty with the specified customer_user_id. 
> If there was no profile with this customer_user_id before — it will be created.

## OpenAPI

```yaml
/api-specs/adapty-api.yaml post /api/v2/server-side-api/purchase/paddle/token/validate/
openapi: 3.1.0
info:
  title: Adapty server-side API
  version: 1.0.0
servers:
  - url: https://api.adapty.io
    description: Production server
paths:
  /api/v2/server-side-api/purchase/paddle/token/validate/:
    post:
      summary: Validate Paddle purchase
      description: |
        Validates a purchase using the provided Paddle token using the credentials of Paddle in your App Settings inside Adapty Dashboard. 
        If the purchase is valid, the transaction history is imported from Paddle to the profile in Adapty with the specified customer_user_id. 
        If there was no profile with this customer_user_id before — it will be created.
      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: Purchase validated successfully
          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: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                no_products_found:
                  summary: No products found
                  value:
                    errors:
                      - No products found
                    error_code: no_products_found
                    status_code: 400
                paddle_api_key_not_found:
                  summary: Paddle API key not found
                  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: Invalid Paddle credentials or purchase not found
                  value:
                    errors:
                      - Invalid Paddle credentials or purchase not found
                    error_code: invalid_paddle_credentials_or_purchase_not_found
                    status_code: 400
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                errors:
                  - Invalid API key
                error_code: unauthorized
                status_code: 401
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
components:
  schemas:
    PaddleValidationRequest:
      type: object
      properties:
        customer_user_id:
          type: string
          description: The ID of your user in your system
        paddle_token:
          type: string
          description: |
            Token of a Paddle object that represents a unique purchase. 
            Could be either a transaction id (txn_...) or a subscription id (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: Source of the error
              errors:
                type: array
                items:
                  type: string
                description: Array of error messages
        error_code:
          type: string
          description: Short error name
        status_code:
          type: integer
          description: HTTP status code
      required:
        - errors
        - error_code
        - status_code
    Profile:
      type: object
      properties:
        app_id:
          type: string
          format: uuid
          description: The internal ID of your app
        profile_id:
          type: string
          format: uuid
          description: Adapty profile ID
        customer_user_id:
          type: string
          nullable: true
          description: The ID of your user in your system
        total_revenue_usd:
          type: number
          format: float
          description: A float value representing the total revenue in USD earned in the profile
        segment_hash:
          type: string
          description: Internal parameter
        timestamp:
          type: integer
          format: int64
          description: Response time in milliseconds, needs for resolve a race condition
        custom_attributes:
          type: array
          items:
            $ref: "#/components/schemas/CustomAttribute"
          description: A maximum of 30 custom attributes to the profile are allowed to be set
        access_levels:
          type: array
          items:
            $ref: "#/components/schemas/AccessLevel"
          description: Array of Access level objects. Empty array if the customer has no access levels
        subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/Subscription"
          description: Array of Subscription objects. Empty array if the customer has no subscriptions
        non_subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/NonSubscription"
          description: Array of Non-Subscription objects. Empty array if the customer has no purchases
      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: The key must be a string with no more than 30 characters. Only letters, numbers, dashes, points, and underscores allowed
        value:
          oneOf:
            - type: string
            - type: number
          description: The attribute value must be no more than 50 characters. Only strings and floats are allowed as values
      required:
        - key
        - value
    AccessLevel:
      type: object
      properties:
        access_level_id:
          type: string
          description: Access level identifier
        store:
          type: string
          description: Store where the access level was purchased
        store_product_id:
          type: string
          description: Product ID in the store
        store_base_plan_id:
          type: string
          nullable: true
          description: Base plan ID in the store
        store_transaction_id:
          type: string
          description: Transaction ID in the store
        store_original_transaction_id:
          type: string
          description: Original transaction ID in the store
        offer:
          allOf:
            - $ref: "#/components/schemas/OfferDTO"
          nullable: true
          description: Offer details, if a promotional or introductory offer was applied
        starts_at:
          type: string
          format: date-time
          nullable: true
          description: When the access level starts
        purchased_at:
          type: string
          format: date-time
          description: When the access level was purchased
        originally_purchased_at:
          type: string
          format: date-time
          description: When the access level was originally purchased
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: When the access level expires
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: When renewal was cancelled
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: When billing issue was detected
        is_in_grace_period:
          type: boolean
          description: Whether the access level is in grace period
        cancellation_reason:
          type: string
          nullable: true
          description: Reason for cancellation
    Subscription:
      type: object
      properties:
        store:
          type: string
          description: Store where the subscription was purchased
        store_product_id:
          type: string
          description: Product ID in the store
        store_base_plan_id:
          type: string
          nullable: true
          description: Base plan ID in the store
        store_transaction_id:
          type: string
          description: Transaction ID in the store
        store_original_transaction_id:
          type: string
          description: Original transaction ID in the store
        offer:
          allOf:
            - $ref: "#/components/schemas/OfferDTO"
          nullable: true
          description: Offer details, if a promotional or introductory offer was applied
        environment:
          type: string
          description: Environment (Sandbox, Production)
        purchased_at:
          type: string
          format: date-time
          description: When the subscription was purchased
        originally_purchased_at:
          type: string
          format: date-time
          description: When the subscription was originally purchased
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: When the subscription expires
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: When renewal was cancelled
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: When billing issue was detected
        is_in_grace_period:
          type: boolean
          description: Whether the subscription is in grace period
        cancellation_reason:
          type: string
          nullable: true
          description: Reason for cancellation
    NonSubscription:
      type: object
      properties:
        purchase_id:
          type: string
          format: uuid
          description: Unique purchase identifier
        store:
          type: string
          description: Store where the purchase was made
        store_product_id:
          type: string
          description: Product ID in the store
        store_base_plan_id:
          type: string
          nullable: true
          description: Base plan ID in the store
        store_transaction_id:
          type: string
          description: Transaction ID in the store
        store_original_transaction_id:
          type: string
          description: Original transaction ID in the store
        purchased_at:
          type: string
          format: date-time
          description: When the purchase was made
        environment:
          type: string
          description: Environment (Sandbox, Production)
        is_refund:
          type: boolean
          description: Whether this is a refund
        is_consumable:
          type: boolean
          description: Whether this is a consumable purchase
    OfferDTO:
      type: object
      properties:
        category:
          type: string
          enum:
            - introductory
            - promotional
            - offer_code
            - win_back
          description: Offer category
        type:
          type: string
          enum:
            - free_trial
            - pay_as_you_go
            - pay_up_front
          description: Offer type
        id:
          type: string
          nullable: true
          description: Offer ID
      required:
        - category
        - type
  securitySchemes:
    apikeyAuth:
      type: apiKey
      name: Authorization
      in: header
      default: Api-Key {Your secret API key}
      description: |
        API requests must be authenticated by your secret API key as the **Authorization** 
        header with the value `Api-Key {your_secret_api_key}`, for example, 
        `Api-Key secret_live_...`. Find this key in the Adapty Dashboard -> 
        **App Settings** -> **General** tab -> **API keys** section.
```
