Skip to content

Conversation

@shuklatushar226
Copy link
Contributor

@shuklatushar226 shuklatushar226 commented Nov 4, 2025

Description

[GLOBALPAY] Connector Integration with Authorize, Psync, Refunds, Rsync, Capture, Void flows

Motivation and Context

Additional Changes

  • This PR modifies the API contract
  • This PR modifies application configuration/environment variables

How did you test it?

  1. Authorize – Manual Capture
    grpcurl -plaintext
    -H 'x-connector: globalpay'
    -H 'x-auth: <AUTH_HEADER>'
    -H 'x-api-key: <API_KEY>'
    -H 'x-merchant-id: <MERCHANT_ID>'
    -H 'x-tenant-id: <TENANT_ID>'
    -d '{
    "request_ref_id": {"id": "test_manual_001"},
    "amount": 1000,
    "currency": "USD",
    "payment_method": {
    "card": {
    "credit": {
    "card_number": {"value": "4263970000005262"},
    "card_exp_month": {"value": "05"},
    "card_exp_year": {"value": "2025"},
    "card_cvc": {"value": "852"},
    "card_network": "VISA"
    }
    }
    },
    "connector_customer_id": "test_customer_manual_001",
    "return_url": "https://www.example.com/return",
    "capture_method": "MANUAL",
    "address": {},
    "auth_type": "NO_THREE_DS",
    "enrolled_for_3ds": false,
    "request_incremental_authorization": false,
    "minor_amount": 1000,
    "browser_info": {
    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
    "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8",
    "language": "en-US",
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "java_enabled": false
    },
    "metadata": {
    "connector_meta_data": "{"merchant_name":"Test Merchant"}"
    }
    }' localhost:8000 ucs.v2.PaymentService/Authorize

  2. Authorize – Automatic Capture
    grpcurl -plaintext
    -H 'x-connector: globalpay'
    -H 'x-auth: <AUTH_HEADER>'
    -H 'x-api-key: <API_KEY>'
    -H 'x-merchant-id: <MERCHANT_ID>'
    -H 'x-tenant-id: <TENANT_ID>'
    -d '{
    "request_ref_id": {"id": "test_auto_002"},
    "amount": 1000,
    "currency": "USD",
    "payment_method": {
    "card": {
    "credit": {
    "card_number": {"value": "4263970000005262"},
    "card_exp_month": {"value": "05"},
    "card_exp_year": {"value": "2025"},
    "card_cvc": {"value": "852"},
    "card_network": "VISA"
    }
    }
    },
    "connector_customer_id": "test_customer_auto_002",
    "return_url": "https://www.example.com/return",
    "capture_method": "AUTOMATIC",
    "address": {},
    "auth_type": "NO_THREE_DS",
    "enrolled_for_3ds": false,
    "request_incremental_authorization": false,
    "minor_amount": 1000,
    "browser_info": {
    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
    "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8",
    "language": "en-US",
    "color_depth": 24,
    "screen_height": 1080,
    "screen_width": 1920,
    "java_enabled": false
    },
    "metadata": {
    "connector_meta_data": "{"merchant_name":"Test Merchant"}"
    }
    }' localhost:8000 ucs.v2.PaymentService/Authorize

  3. Get Payment (PSync)
    grpcurl -plaintext
    -H 'x-connector: globalpay'
    -H 'x-auth: <AUTH_HEADER>'
    -H 'x-api-key: <API_KEY>'
    -H 'x-merchant-id: <MERCHANT_ID>'
    -H 'x-tenant-id: <TENANT_ID>'
    -d '{
    "transaction_id": {"id": "<TRANSACTION_ID>"},
    "request_ref_id": {"id": "test_get_003"}
    }' localhost:8000 ucs.v2.PaymentService/Get

  4. Capture Payment
    grpcurl -plaintext
    -H "x-connector: globalpay"
    -H "x-auth: <AUTH_HEADER>"
    -H "x-api-key: <API_KEY>"
    -H "x-key1: "
    -H "x-merchant-id: <MERCHANT_ID>"
    -H "x-tenant-id: <TENANT_ID>"
    -d '{
    "transaction_id": {"id": "<TRANSACTION_ID>"},
    "request_ref_id": {"id": "test_capture_004"},
    "amount_to_capture": 1000,
    "currency": "USD"
    }'
    localhost:8000 ucs.v2.PaymentService/Capture

  5. Void Payment
    grpcurl -plaintext
    -H "x-connector: globalpay"
    -H "x-auth: <AUTH_HEADER>"
    -H "x-api-key: <API_KEY>"
    -H "x-key1: "
    -H "x-merchant-id: <MERCHANT_ID>"
    -H "x-tenant-id: <TENANT_ID>"
    -d '{
    "transaction_id": {"id": "<TRANSACTION_ID>"},
    "request_ref_id": {"id": "test_void_005"},
    "cancellation_reason": "Testing void flow",
    "amount": 1000,
    "currency": "USD"
    }'
    localhost:8000 ucs.v2.PaymentService/Void

  6. Refund Payment
    grpcurl -plaintext
    -H "x-connector: globalpay"
    -H "x-auth: <AUTH_HEADER>"
    -H "x-api-key: <API_KEY>"
    -H "x-key1: "
    -H "x-merchant-id: <MERCHANT_ID>"
    -H "x-tenant-id: <TENANT_ID>"
    -d '{
    "request_ref_id": {"id": "test_refund_006"},
    "refund_id": "refund_test_006",
    "transaction_id": {"id": "<TRANSACTION_ID>"},
    "payment_amount": 1000,
    "currency": "USD",
    "minor_payment_amount": 1000,
    "refund_amount": 500,
    "minor_refund_amount": 500,
    "reason": "Testing refund flow",
    "capture_method": "MANUAL",
    "state": {
    "access_token": {
    "token": "<ACCESS_TOKEN>",
    "expires_in_seconds": 86399
    }
    }
    }'
    localhost:8000 ucs.v2.PaymentService/Refund

  7. Get Refund Status (RSync)
    grpcurl -plaintext
    -H "x-connector: globalpay"
    -H "x-auth: <AUTH_HEADER>"
    -H "x-api-key: <API_KEY>"
    -H "x-key1: "
    -H "x-merchant-id: <MERCHANT_ID>"
    -H "x-tenant-id: <TENANT_ID>"
    -d '{
    "request_ref_id": {"id": "test_rsync_007"},
    "transaction_id": {"id": "<TRANSACTION_ID>"},
    "refund_id": "<REFUND_ID>",
    "state": {
    "access_token": {
    "token": "<ACCESS_TOKEN>",
    "expires_in_seconds": 86399
    }
    }
    }'
    localhost:8000 ucs.v2.RefundService/Get

Authorize flow body diff:

Screenshot 2025-11-10 at 10 09 54 AM

Refund flow body diff:

Screenshot 2025-11-10 at 11 00 38 AM

Void flow body diff:

Screenshot 2025-11-10 at 11 00 38 AM

Capture flow body diff:

Screenshot 2025-11-10 at 11 03 30 AM

@shuklatushar226 shuklatushar226 requested review from a team as code owners November 4, 2025 06:52
@shuklatushar226 shuklatushar226 requested a review from a team as a code owner November 5, 2025 11:41
@shuklatushar226 shuklatushar226 force-pushed the globalpay-grace-implementation branch from 22520b5 to 1805217 Compare November 10, 2025 08:13

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this

Comment on lines +153 to +157
headers::X_GP_IDEMPOTENCY.to_string(),
req.resource_common_data
.connector_request_reference_id
.clone()
.into(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required?

Funded,
Pending,
Initiated,
#[serde(rename = "FOR_REVIEW")]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename not needed

pub expiry_year: Secret<String>,
pub cvv: Secret<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cvv_indicator: Option<String>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont hardcode cvv_indicator to none

// Capture Request Structure
#[derive(Debug, Serialize)]
pub struct GlobalpayCaptureRequest {
pub amount: Option<StringMinorUnit>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont make amount option

Comment on lines +471 to +474
let _transaction_id = item
.request
.get_connector_transaction_id()
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this added?

#[serde(skip_serializing_if = "Option::is_none")]
pub amount: Option<StringMinorUnit>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use currency enum

Comment on lines +515 to +526
pub struct GlobalpayPaymentMethodResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub card: Option<GlobalpayCardResponse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub apm: Option<GlobalpayApmResponse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Secret<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<String>,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont use skip_serializing_if = "Option::is_none"
for response structs

Comment on lines +106 to +126
if should_do_access_token {
let access_token_data = match cached_access_token {
Some((token, expires_in)) => {
// Use cached token
tracing::info!("Using cached access token from Hyperswitch");
Some(AccessTokenResponseData {
access_token: token,
token_type: None,
expires_in,
})
}
None => {
// OAuth tokens must be provided for refund flows
tracing::error!(
"OAuth access token required but not provided in request state"
);
return Err(tonic::Status::internal(
"OAuth access token required but not provided for refund operation",
));
}
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if we can directly move the should_do_access_token logic to implement_connector_operation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants