Skip to main content
Version: ChatLead

Payment Service with Paypal Api's

Author(s)

  • Reshmi Karan

Last Updated Date

2025-03-07


SRS References


Version History

VersionDateChangesAuthor
1.02025-02-19Initial draftReshmi Karan
1.12025-03-05Doc fixesAyon Das
22025-03-05Payment workflowReshmi Karan

Feature Overview

Objective:

Our existing PayPal SDK has been deprecated, requiring us to upgrade to PayPal’s latest APIs (v2 and v3). This upgrade ensures smooth and seamless payment processing within our software, maintaining reliability and compliance with PayPal's latest standards. The implementation includes authentication, payment vaulting, bill payment, and refund handling to enhance the overall payment workflow.

Scope:

  1. Implement OAuth Authentication – Secure integration using PayPal’s OAuth-based authentication.
  2. Generate & Store subscribers' Credit Card Vault – Implement functionality to securely store subscribers' credit card vault.
  3. Create Orders & Process Payments with Vault – Enable order creation and seamless transactions using stored vault.
  4. Delete Vault – Provide functionality to remove stored vault.
  5. Create Orders & Process One-Time Payments (Without Vault) – Support transactions without storing credit card details.
  6. Refund Processing – Implement refund functionality.

Dependencies:

Paypal REST API v2/v3.


Requirements

  1. Functional Requirements : These define what the system should do:

    • OAuth Authentication: The system must authenticate with PayPal using OAuth 2.0. Access tokens must be obtained and refreshed automatically.

    • Payment Method Token Generation: Using the Payment Method Tokens API, generate a Setup Token from the payment source, add it to the Vault, and then create a Payment Token to obtain a vault_id for future transactions. And get Credit card details using this token.

    • Payment Vaulting: Subscriber must be able to securely store their credit card details in PayPal’s vault. The system must retrieve vaulted payment methods when needed. Subscriber must have the ability to delete their stored payment methods. Check particular payment status.

    • Payment Processing: The system must support bill payment using both vaulted and non-vaulted payment methods. The system must process payments using stored payment methods (vaulted). The system must process one-time payments without storing card details.

    • Refund Handling: The system must allow full and partial refunds for bill. Refunds should be tracked and recorded in the system.

  2. Non-Functional Requirements : These define the quality and operational characteristics of the system:

    • Security: All payment data must be securely transmitted using HTTPS. OAuth tokens and sensitive payment details must be encrypted and stored securely. The system must comply with PCI-DSS standards where applicable.

    • Performance: Payment processing must occur in real-time with minimal latency. Vault operations (store, retrieve, delete) should complete within an acceptable response time.

    • Scalability: The system should handle multiple concurrent payment requests efficiently. It must be capable of processing high transaction volumes without degradation.

    • Logging & Monitoring: All API interactions (authentication, payments, refunds, vault operations) should be logged for auditing. Errors and failures should trigger alerts for quick troubleshooting.

    • Compliance: The system must comply with PayPal’s latest API standards and guidelines.


Design Specifications

(Provide detailed design specifications, including UI/UX designs, API interfaces, and any other relevant architectural details.)

  • UI/UX Design:
    (Include wireframes, mockups, or links to design files.)

  • Data Models:

    public class PayPalTokenResponse
    {
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
    }
    public class PayPalPaymentOnetimeRequest
    {
    [JsonPropertyName("intent")]
    public string Intent { get; set; }
    [JsonPropertyName("purchase_units")]
    public List<PurchaseUnit> PurchaseUnits { get; set; }
    [JsonPropertyName("payment_source")]
    public PaymentSource PaymentSource { get; set; }
    }
    public class PaymentSource
    {
    [JsonPropertyName("card")]
    public Card Card { get; set; }
    }

    public class Card
    {
    [JsonPropertyName("number")]
    public string Number { get; set; }
    [JsonPropertyName("expiry")]
    public string Expiry { get; set; }
    [JsonPropertyName("name")]
    public string Name { get; set; }
    [JsonPropertyName("last_digits")]
    public string LastDigits { get; set; }
    [JsonPropertyName("security_code")]
    public string Cvv { get; set; }
    [JsonPropertyName("billing_address")]
    public BillingAddress BillingAddress { get; set; }
    [JsonPropertyName("type")]
    public string Type { get; set; }
    [JsonPropertyName("brand")]
    public string Brand { get; set; }
    }
    public class BillingAddress
    {
    [JsonPropertyName("country_code")]
    public string CountryCode { get; set; }
    }
    public class PurchaseUnit
    {
    [JsonPropertyName("reference_id")]
    public string ReferenceId { get; set; }
    //[JsonPropertyName("invoice_id")]
    //public string InvoiceId { get; set; }
    [JsonPropertyName("amount")]
    public PaymentAmount Amount { get; set; }
    [JsonPropertyName("items")]
    public List<PaymentItem> Items { get; set; }
    }
    public class PaymentAmount
    {
    [JsonPropertyName("currency_code")]
    public string CurrencyCode { get; set; }
    [JsonPropertyName("value")]
    public string Value { get; set; }
    [JsonPropertyName("breakdown")]
    public Breakdown Breakdown { get; set; }
    }

    public class Breakdown
    {
    [JsonPropertyName("item_total")]
    public AmountWithCurrency ItemTotal { get; set; }
    [JsonPropertyName("shipping")]
    public AmountWithCurrency Shipping { get; set; }
    [JsonPropertyName("tax_total")]
    public AmountWithCurrency TaxTotal { get; set; }
    }

    public class AmountWithCurrency
    {
    [JsonPropertyName("currency_code")]
    public string CurrencyCode { get; set; }
    [JsonPropertyName("value")]
    public string Value { get; set; }
    }

    public class PaymentItem
    {
    [JsonPropertyName("name")]
    public string Name { get; set; }
    [JsonPropertyName("description")]
    public string Description { get; set; }
    [JsonPropertyName("unit_amount")]
    public AmountWithCurrency UnitAmount { get; set; }
    [JsonPropertyName("quantity")]
    public string Quantity { get; set; }
    [JsonPropertyName("sku")]
    public string Sku { get; set; }
    }
    public class PayPalPaymentResponse
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    [JsonPropertyName("intent")]
    public string Intent { get; set; }
    [JsonPropertyName("status")]
    public string Status { get; set; }
    [JsonPropertyName("payment_source")]
    public PaymentSource PaymentSource { get; set; }
    [JsonPropertyName("purchase_units")]
    public List<PurchaseUnitResponse> PurchaseUnits { get; set; }
    [JsonPropertyName("create_time")]
    public DateTime CreateTime { get; set; }
    [JsonPropertyName("update_time")]
    public DateTime UpdateTime { get; set; }
    }
    public class PurchaseUnitResponse : PurchaseUnit
    {
    [JsonPropertyName("payments")]
    public Payments Payments { get; set; }
    [JsonPropertyName("payee")]
    public Payee Payee { get; set; }
    }
    public class Payee
    {
    [JsonPropertyName("email_address")]
    public string EmailAddress { get; set; }

    [JsonPropertyName("merchant_id")]
    public string MerchantId { get; set; }
    }
    public class Payments
    {
    [JsonPropertyName("captures")]
    public List<Capture> Captures { get; set; }
    }

    public class Capture
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    [JsonPropertyName("status")]
    public string Status { get; set; }
    [JsonPropertyName("amount")]
    public AmountWithCurrency Amount { get; set; }
    [JsonPropertyName("final_capture")]
    public bool FinalCapture { get; set; }
    [JsonPropertyName("disbursement_mode")]
    public string DisbursementMode { get; set; }
    [JsonPropertyName("seller_protection")]
    public SellerProtection SellerProtection { get; set; }
    [JsonPropertyName("seller_receivable_breakdown")]
    public SellerReceivableBreakdown SellerReceivableBreakdown { get; set; }
    [JsonPropertyName("invoice_id")]
    public string InvoiceId { get; set; }
    [JsonPropertyName("create_time")]
    public DateTime CreateTime { get; set; }
    [JsonPropertyName("update_time")]
    public DateTime UpdateTime { get; set; }
    [JsonPropertyName("network_transaction_reference")]
    public NetworkTransactionReference NetworkTransactionReference { get; set; }
    [JsonPropertyName("processor_response")]
    public ProcessorResponse ProcessorResponse { get; set; }
    }

    public class SellerProtection
    {
    [JsonPropertyName("status")]
    public string Status { get; set; }
    }

    public class SellerReceivableBreakdown
    {
    [JsonPropertyName("gross_amount")]
    public AmountWithCurrency GrossAmount { get; set; }
    [JsonPropertyName("net_amount")]
    public AmountWithCurrency NetAmount { get; set; }
    }

    public class NetworkTransactionReference
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    [JsonPropertyName("network")]
    public string Network { get; set; }
    }

    public class ProcessorResponse
    {
    [JsonPropertyName("avs_code")]
    public string AvsCode { get; set; }
    [JsonPropertyName("cvv_code")]
    public string CvvCode { get; set; }
    [JsonPropertyName("response_code")]
    public string ResponseCode { get; set; }
    }
    public class PayPalPaymentCaptureResponse
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }

    [JsonPropertyName("amount")]
    public AmountWithCurrency Amount { get; set; }

    [JsonPropertyName("final_capture")]
    public bool FinalCapture { get; set; }

    [JsonPropertyName("seller_protection")]
    public SellerProtection SellerProtection { get; set; }

    [JsonPropertyName("disbursement_mode")]
    public string DisbursementMode { get; set; }

    [JsonPropertyName("seller_receivable_breakdown")]
    public SellerReceivableBreakdown SellerReceivableBreakdown { get; set; }

    [JsonPropertyName("status")]
    public string Status { get; set; }

    [JsonPropertyName("processor_response")]
    public ProcessorResponse ProcessorResponse { get; set; }

    [JsonPropertyName("supplementary_data")]
    public SupplementaryData SupplementaryData { get; set; }

    [JsonPropertyName("payee")]
    public Payee Payee { get; set; }

    [JsonPropertyName("create_time")]
    public DateTime CreateTime { get; set; }

    [JsonPropertyName("update_time")]
    public DateTime UpdateTime { get; set; }

    [JsonPropertyName("network_transaction_reference")]
    public NetworkTransactionReference NetworkTransactionReference { get; set; }
    }

    public class SupplementaryData
    {
    [JsonPropertyName("related_ids")]
    public RelatedIds RelatedIds { get; set; }
    }

    public class RelatedIds
    {
    [JsonPropertyName("order_id")]
    public string OrderId { get; set; }
    }
    public class PaypalRefundRequest
    {
    [JsonPropertyName("amount")]
    public AmountWithCurrency Amount { get; set; }
    [JsonPropertyName("note_to_payer")]
    public string NoteToPayer { get; set; }
    }
    public class RefundResponse
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    [JsonPropertyName("amount")]
    public AmountWithCurrency Amount { get; set; }
    [JsonPropertyName("note_to_payer")]
    public string NoteToPayer { get; set; }
    [JsonPropertyName("seller_payable_breakdown")]
    public SellerPayableBreakdown SellerPayableBreakdown { get; set; }
    [JsonPropertyName("invoice_id")]
    public string InvoiceId { get; set; }
    [JsonPropertyName("status")]
    public string Status { get; set; }
    [JsonPropertyName("create_time")]
    public DateTime CreateTime { get; set; }
    [JsonPropertyName("update_time")]
    public DateTime UpdateTime { get; set; }
    }
    public class SellerPayableBreakdown
    {
    [JsonPropertyName("gross_amount")]
    public AmountWithCurrency GrossAmount { get; set; }
    [JsonPropertyName("paypal_fee")]
    public AmountWithCurrency PayPalFee { get; set; }
    [JsonPropertyName("net_amount")]
    public AmountWithCurrency NetAmount { get; set; }
    [JsonPropertyName("total_refunded_amount")]
    public AmountWithCurrency TotalRefundedAmount { get; set; }
    }
    public class CreditCardInfo
    {
    [JsonPropertyName("payment_source")]
    public PaymentSource PaymentSource { get; set; }
    }
    public class CWSubscriberAddCCResponse
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    [JsonPropertyName("customer")]
    public Customer Customer { get; set; }
    [JsonPropertyName("status")]
    public string Status { get; set; }
    [JsonPropertyName("payment_source")]
    public PaymentSource PaymentSource { get; set; }
    }
    public class Customer
    {
    [JsonPropertyName("id")]
    public string Id { get; set; }
    }
    public class PayPalPaymentRequest
    {
    [JsonPropertyName("intent")]
    public string Intent { get; set; }
    [JsonPropertyName("purchase_units")]
    public List<PurchaseUnit> PurchaseUnits { get; set; }
    [JsonPropertyName("payment_source")]
    public PaymentSourceInfo PaymentSource { get; set; }
    }
    public class PaymentSourceInfo
    {
    [JsonPropertyName("card")]
    public CardInfo Card { get; set; }
    }

    public class CardInfo
    {
    [JsonPropertyName("vault_id")]
    public string VaultId { get; set; }
    }

  • API Interfaces:

    EndpointMethodParametersResponseResponse Status Codes
    /v1/oauth2/tokenPOSTgrant_type= client_credentials,
    Client ID,
    Client Secret
    PayPalAuthResponse201, 204, 500
    /v3/vault/setup-tokensPOSTCWSubscriberAddCCRequestCWSubscriberAddCCResponse200,201,400,403,422,500
    /v3/vault/payment-tokensPOSTPaymentTokenRequestPaymentTokenResponse200,201,400,403,422,500
    /v3/vault/payment-tokens/{payment_token_id}GETPaymentTokenRequestPaymentTokenResponse200,201,400,403,422,500
    /v3/vault/payment-tokens/{payment_token_id}DELETEpayment_token_id (required, string)204, 400,403, 500
    /v2/checkout/ordersPOSTPayPalPaymentRequest(pass in header PayPal-Request-Id)PayPalPaymentResponse200, 201, 400, 422
    /v2/checkout/ordersPOSTPayPalPaymentOnetimeRequestPayPalPaymentResponse200, 201, 400, 422
    /v2/checkout/orders/{{order_id}}GetPayPalPaymentResponse200, 404
    /v2/payments/captures/{capture_id}GetPayPalCaptureDetails200, 404
    /v2/payments/captures/{capture_id}/refundPOSTRefundRequest /RefundResponse200, 201,400,401, 403, 404,409 ,422, 500
  • Third-Party Integrations:
    Paypal Api

  • Workflow:

    • CC Save Only (Tokenization Process) Purpose: Validate the credit card without charging the user.

      Steps:

      1. User Action: User chooses to save their card.
      2. Authorization & Deduction: System deducts $1 from the user's card.
      3. Immediate Refund: Refund the $1 to prevent actual charge.
      4. Get Vault Token: Payment gateway returns a vault token (e.g., a tokenizedCC reference).
      5. Save Vault Token: The system stores the token securely for futuretransactions.
      6. User Notified: User is informed that the card has been saved successfully.

      Workflow Workflow

    • CC Save & Pay Purpose: Save the card first, then process a payment using the stored card.

      Steps:

      1. User Action: User chooses to save card and make a payment.
      2. Authorization & Deduction: System deducts $1 from the user's card.
      3. Immediate Refund: Refund the $1 to prevent actual charge.
      4. Get Vault Token: Payment gateway returns a vault token (tokenized CC reference).
      5. Save Vault Token: The system stores the token securely.
      6. Payment Processing: The system uses the vault token to charge the actual transaction amount. If successful, payment is confirmed.
      7. User Notified: User receives a confirmation for both card save & payment.
    • One-Time Payment with Saved CC Purpose: Directly process a payment using a previously saved card.

      Steps:

      1. User Action: User selects a previously saved card for payment.
      2. System Retrieves Vault Token: Fetches the stored token for the user’s card.
      3. Payment Processing: The system sends the vault token to the payment gateway for charging. If successful, payment is confirmed.
      4. User Notified: User receives a confirmation of successful payment.

Development Tasks & Estimates

NoTask NameEstimate (Hours)DependenciesNotes
1Authorization using oauth2.0 and save memory and database6 hours
2Deduct $1 from user's cc(using new workflow)21 hours
3Refund $1 to user (using new workflow)21 hours
4Generate vault and save16 hours
5Onetime payment21 hours
6Delete CC14 hours
7CC save and payment28 hours
8Refund14 hours
9Testing35 hours
10Total182 hoursDependency 3Any notes here

Testing & Quality Assurance

(Outline the testing strategy and quality assurance measures for the feature.)

  • Unit Tests:
    (List the unit tests that will be written for this feature.)

  • Integration Tests:
    (Describe how integration testing will be conducted.)

  • Acceptance Criteria:
    (Define the criteria that must be met for the feature to be considered complete.)

  • Testing Tools:
    (List any tools that will be used for testing.)


Deployment Considerations

(Describe any deployment considerations, including environment configurations, feature toggles, or migration steps.)

  • Configuration Changes:
    Save and Send country code is mandatory to add CC or make a payment through CC. Additionally, the version is mandatory as our system now supports both old and new PayPal integrations.

    Paymentportal DB
    alter table [tblSubscriberVault] add CountryCode nvarchar(100) default 'US'
    alter table [dbo].[tblSubscriberVault_Audit] add CountryCode nvarchar(100) default 'US'
    alter table [dbo].[tblSubscriberVault] add ApiIntegration nvarchar(100) default 'NEW'
    alter table [dbo].[tblSubscriberVault_Audit] add ApiIntegration nvarchar(100) default 'NEW'
    trgtblSubscriberVault
    spGetCreditCardInfoByTokenId_V2
    spGetCreditCardInfoByTokenId
    spAddCreditCardToVault
    spCreateTransactionToken
    spGenerateToken
    spGenerateToken_V2
    spGetActiveCreditCardInfoBySubscriberId
    spGetCreditCardInfoBySubscriberIdAndTokenId
    spGetActiveVaultIdBySubscriberIds
    spGetActiveVaultID
    spGetPaypalVersion

    alter table tblOneTimePaymentLog add CountryCode nvarchar(100) default 'US'
    spSetOneTimePaymentLog
    spGetOTCCInvoiceBaseInfoByTokenId

    Backend -mastersvr
    spGetSubscriberMinimumInfoFromMaster

  • Rollout Plan:


Risks & Mitigations

RiskImpactLikelihoodMitigation Strategy
Inconsistent behavior between old and new workflowsHighHighCreate setting for old and new

Review & Approval

(Include a section for review and approval by stakeholders.)

  • Reviewer:

    • Ayon Das
    • Abhishak Kumar Roy
  • Approval Date:
    2025-03-05


Notes

We choose intent type CAPTURE.

  • CAPTURE - The merchant intends to capture payment immediately after the customer makes a payment.

  • AUTHORIZE - The merchant intends to authorize a payment and place funds on hold after the customer makes a payment. Authorized payments are best captured within three days of authorization but are available to capture for up to 29 days. After the three-day honor period, the original authorized payment expires and you must re-authorize the payment. You must make a separate request to capture payments on demand. This intent is not supported when you have more than one purchase_unit within your order.