Skip to content

Add support for partner referral #53

@oOLooCoreZOo

Description

@oOLooCoreZOo

Hi,

It would be awesome if you could add support for the partner referral api.
I am currently using this code to create partner referrals that seems to work.

import { ApiResponse, CustomError, LinkDescription } from "@paypal/paypal-server-sdk";

// WORKAROUND START
import { BaseController } from "@paypal/paypal-server-sdk/dist/cjs/controllers/baseController";
import { RequestOptions } from "@paypal/paypal-server-sdk/dist/cjs/core";
import { linkDescriptionSchema } from "@paypal/paypal-server-sdk/dist/cjs/models/linkDescription";
import { array, lazy, object, optional, Schema, string, boolean } from '@paypal/paypal-server-sdk/dist/cjs/schema';
// WORKAROUND END

/**
 * Models for partner referral components
 */
export interface IndividualOwner {
    name: string;
    email: string;
}
export const individualOwnerSchema: Schema<IndividualOwner> = object({
    name: ['name', string()],
    email: ['email', string()]
});

export interface BusinessEntity {
    businessName: string;
    businessType: string;
}
export const businessEntitySchema: Schema<BusinessEntity> = object({
    businessName: ['business_name', string()],
    businessType: ['business_type', string()]
});

export interface PartnerConfigurationOverride {
    /** The URL to which to redirect the customer upon completion of the onboarding process. */
    returnUrl: string;
    /** The description of the return URL. */
    returnUrlDescription?: string;
    /** Whether to show an add credit card page. */
    showAddCreditCard?: boolean;
}
export const partnerConfigurationOverrideSchema: Schema<PartnerConfigurationOverride> = object({
    returnUrl: ['return_url', string()],
    returnUrlDescription: ['return_url_description', optional(string())],
    showAddCreditCard: ['show_add_credit_card', optional(boolean())]
});

export interface LegalConsent {
    type: string;
    granted: boolean;
}
export const legalConsentSchema: Schema<LegalConsent> = object({
    type: ['type', string()],
    granted: ['granted', boolean()]
});

export interface FinancialInstruments {
    instrumentType: string;
}
export const financialInstrumentsSchema: Schema<FinancialInstruments> = object({
    instrumentType: ['instrument_type', string()]
});

export interface PayoutAttributes {
    payoutCurrency: string;
}
export const payoutAttributesSchema: Schema<PayoutAttributes> = object({
    payoutCurrency: ['payout_currency', string()]
});

/**
 * PartnerReferralRequest for creating a referral
 */
export interface PartnerReferralRequest {
    individualOwners?: IndividualOwner[];
    businessEntity?: BusinessEntity;
    trackingId?: string;
    operations: Operation[];
    products?: string[];
    capabilities?: string[];
    outsideProcessDependencies?: any[];
    legalConsents: LegalConsent[];
    email?: string;
    preferredLanguageCode?: string;
    partnerConfigOverride?: PartnerConfigurationOverride;
    financialInstruments?: FinancialInstruments;
    payoutAttributes?: PayoutAttributes;
    legalCountryCode?: string;
}

/**
 * PartnerReferralResponse returned from create or get
 */
export interface PartnerReferralResponse {
    partnerReferralId?: string;
    submitterPayerId?: string;
    submitterClientId?: string;
    links: LinkDescription[];
    actionUrl?: string;
    referralData?: PartnerReferralRequest;
}


export interface FirstPartyDetails {
  features: string[];
  sellerNonce?: string;
}

export interface ThirdPartyDetails {
  features: string[];
  signupMode?: string;
  organization?: string;
}

export interface RestApiIntegration {
  integrationMethod: string;
  integrationType: string;
  firstPartyDetails?: FirstPartyDetails;
  thirdPartyDetails?: ThirdPartyDetails;
}

export interface ApiIntegrationPreference {
  restApiIntegration?: RestApiIntegration;
  offlineOnboardingPreference?: object;
  billingAgreement?: object;
}

export interface Operation {
  operation: string;
  apiIntegrationPreference?: ApiIntegrationPreference;
  // …plus offlineOnboardingPreference, billingAgreement if used directly
}

export const firstPartyDetailsSchema: Schema<FirstPartyDetails> = object({
  features: ['features', array(string())],
  sellerNonce: ['seller_nonce', optional(string())],
});

export const thirdPartyDetailsSchema: Schema<ThirdPartyDetails> = object({
  features: ['features', array(string())],
  signupMode: ['signup_mode', optional(string())],
  organization: ['organization', optional(string())],
});

export const restApiIntegrationSchema: Schema<RestApiIntegration> = object({
  integrationMethod: ['integration_method', string()],
  integrationType: ['integration_type', string()],
  firstPartyDetails: ['first_party_details', optional(lazy(() => firstPartyDetailsSchema))],
  thirdPartyDetails: ['third_party_details', optional(lazy(() => thirdPartyDetailsSchema))],
});

export const apiIntegrationPreferenceSchema: Schema<ApiIntegrationPreference> = object({
  restApiIntegration: ['rest_api_integration', optional(lazy(() => restApiIntegrationSchema))],
  offlineOnboardingPreference: ['offline_onboarding_preference', optional(object({}))],
  billingAgreement: ['billing_agreement', optional(object({}))],
});

export const operationSchema: Schema<Operation> = object({
  operation: ['operation', string()],
  apiIntegrationPreference: ['api_integration_preference', optional(lazy(() => apiIntegrationPreferenceSchema))],
  // add offlineOnboardingPreference, billingAgreement schemas as needed…
});

/**
 * partnerReferralRequestSchema
 * POST /v2/customer/partner-referrals
 */
export const partnerReferralRequestSchema: Schema<PartnerReferralRequest> = object({
    individualOwners: ['individual_owners', optional(array(lazy(() => individualOwnerSchema)))],
    businessEntity: ['business_entity', optional(lazy(() => businessEntitySchema))],
    trackingId: ['tracking_id', optional(string())],
    operations: ['operations', array(lazy(() => operationSchema))],
    products: ['products', optional(array(string()))],
    capabilities: ['capabilities', optional(array(string()))],
    outsideProcessDependencies: ['outside_process_dependencies', optional(array(lazy(() => object({ /* define dependency fields here */ }))))],
    legalConsents: ['legal_consents', array(lazy(() => legalConsentSchema))],
    email: ['email', optional(string())],
    preferredLanguageCode: ['preferred_language_code', optional(string())],
    partnerConfigOverride: ['partner_config_override', optional(lazy(() => partnerConfigurationOverrideSchema))],
    financialInstruments: ['financial_instruments', optional(lazy(() => financialInstrumentsSchema))],
    payoutAttributes: ['payout_attributes', optional(lazy(() => payoutAttributesSchema))],
    legalCountryCode: ['legal_country_code', optional(string())]
});

/**
 * partnerReferralResponseSchema
 * GET /v2/customer/partner-referrals/{partner_referral_id}
 */
export const partnerReferralResponseSchema: Schema<PartnerReferralResponse> = object({
    partnerReferralId: ['partner_referral_id', optional(string())],
    submitterPayerId: ['submitter_payer_id', optional(string())],
    submitterClientId: ['submitter_client_id', optional(string())],
    links: ['links', array(lazy(() => linkDescriptionSchema))],
    actionUrl: ['action_url', optional(string())],
    referralData: ['referral_data', optional(lazy(() => partnerReferralRequestSchema))]
});

/**
 * PartnerReferralController
 */
export class PartnerReferralController extends BaseController {
    /**
     * Create a new partner referral.
     * POST /v2/customer/partner-referrals
     */
    public async createPartnerReferral(
        {
            body
        }: {
            body: PartnerReferralRequest
        },
        requestOptions?: RequestOptions
    ): Promise<ApiResponse<PartnerReferralResponse>> {
        const req = this.createRequest("POST");
        const mapped = req.prepareArgs({
            body: [body, partnerReferralRequestSchema]
        });
        req.header("Content-Type", "application/json");
        req.json(mapped.body);
        req.appendTemplatePath`/v2/customer/partner-referrals`;
        req.throwOn(
            400,
            CustomError,
            "Bad Request: malformed or schema-violating payload."
        );
        req.throwOn(
            403,
            CustomError,
            "Forbidden: insufficient permissions."
        );
        req.throwOn(
            422,
            CustomError,
            "Unprocessable Entity: semantic or business-validation failure."
        );
        req.throwOn(
            500,
            CustomError,
            "Internal Server Error."
        );
        req.defaultToError(
            CustomError,
            "Unexpected error response."
        );
        req.authenticate([{ oauth2: true }]);
        return req.callAsJson(
            partnerReferralResponseSchema,
            requestOptions
        );
    }

    /**
     * Retrieve an existing partner referral by ID.
     * GET /v2/customer/partner-referrals/{partner_referral_id}
     */
    public async getPartnerReferral(
        partnerReferralId: string,
        requestOptions?: RequestOptions
    ): Promise<ApiResponse<PartnerReferralResponse>> {
        const req = this.createRequest("GET");
        req.appendTemplatePath`/v2/customer/partner-referrals/${partnerReferralId}`;
        req.throwOn(
            400,
            CustomError,
            "Bad Request: invalid referral ID."
        );
        req.throwOn(
            403,
            CustomError,
            "Forbidden: insufficient permissions."
        );
        req.throwOn(
            404,
            CustomError,
            "Not Found: referral ID does not exist."
        );
        req.throwOn(
            500,
            CustomError,
            "Internal Server Error."
        );
        req.defaultToError(
            CustomError,
            "Unexpected error response."
        );
        req.authenticate([{ oauth2: true }]);
        return req.callAsJson(
            partnerReferralResponseSchema,
            requestOptions
        );
    }
}

Please note that the data structures mostly use strings only, even if internal data already exists.

Best regards.
Chris

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions