Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fawry connector scaffold #1643

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Conversation

altitude
Copy link
Member

@altitude altitude commented Aug 9, 2024

No description provided.

Signed-off-by: Clément Salaün <[email protected]>
@altitude altitude requested a review from a team as a code owner August 9, 2024 11:43
Copy link
Contributor

coderabbitai bot commented Aug 9, 2024

Walkthrough

The updates introduce a comprehensive payment processing connector for the Fawry payment system, featuring structures for client requests, invoicing, notifications, and configuration management. New methods facilitate secure request signing, enhance task management, and streamline asynchronous notification handling, improving the overall modularity and error handling in payment workflows.

Changes

Files Change Summary
components/payments/cmd/connectors/internal/connectors/fawry/client/{client.go, invoice.go, notification.go} Introduce Client struct for secure request signing, InvoiceInfo for invoice details, and Notification for payment notifications.
components/payments/cmd/connectors/internal/connectors/fawry/{config.go, connector.go, currencies.go} Implement Config struct for configuration management, Connector for task handling, and currency mapping for decimal values.
components/payments/cmd/connectors/internal/connectors/fawry/{ingest_payment.go, loader.go} Define taskIngestPayment as a placeholder and implement Loader for connector initialization and routing.
components/payments/cmd/connectors/internal/connectors/fawry/{resolve.go, webhook.go} Introduce task management structures and functionality, and implement Webhook for handling payment notifications.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Connector
    participant Loader
    participant Webhook

    Client->>Connector: Initiate Payment Request
    Connector->>Loader: Load Configuration
    Loader->>Webhook: Route Notification
    Webhook->>Connector: Process Notification
    Connector->>Client: Send Response
Loading

🐇 "Hippity hop, our code's a delight,
New structures and functions, everything's tight!
With payments that flow, and tasks that align,
Fawry's now ready, oh how it will shine!
Let's celebrate changes, so fresh and so bright,
In the world of payments, we’re taking flight!" 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Outside diff range, codebase verification and nitpick comments (6)
components/payments/cmd/connectors/internal/connectors/fawry/client/client.go (1)

20-22: Consider adding documentation for the Init method.

The Init method currently returns nil, and it might be beneficial to add documentation or comments indicating its intended use or future expansion.

+ // Init initializes the client. Currently, it is a placeholder for future setup logic.
components/payments/cmd/connectors/internal/connectors/fawry/config.go (1)

12-14: Consider Adding Custom Error Handling for Marshaling.

The Marshal method uses the default json.Marshal, which might not provide detailed error information. Consider adding custom error handling if detailed logging or error tracing is required.

components/payments/cmd/connectors/internal/connectors/fawry/webhook.go (2)

15-16: Consider adding fields to the Webhook struct.

The Webhook struct is currently empty. If there are any configurations or dependencies needed for the webhook, consider adding them as fields.


30-30: Ensure Context Cancellation in fawry/webhook.go

The context created with contextutil.DetachedWithTimeout in fawry/webhook.go does not capture the cancel function, which may result in resource leaks. Please ensure that the cancel function is captured and called appropriately to manage the context lifecycle.

  • components/payments/cmd/connectors/internal/connectors/fawry/webhook.go: Line 30
Analysis chain

Check for context cancellation.

The context created with contextutil.DetachedWithTimeout ignores the cancel function. Ensure that the context is properly managed and canceled if necessary to avoid resource leaks.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if the context cancellation is handled in the codebase.

# Test: Search for context cancellation handling. Expect: Proper cancellation logic.
rg --type go 'contextutil.DetachedWithTimeout'

Length of output: 2967


Script:

#!/bin/bash
# Check if the cancel function is called in files where it is captured

# Search for the usage of the cancel function in the atlar/task_payments.go file
rg --type go 'cancel\(\)' components/payments/cmd/connectors/internal/connectors/atlar/task_payments.go

# Search for the usage of the cancel function in the atlar/task_fetch_transactions.go file
rg --type go 'cancel\(\)' components/payments/cmd/connectors/internal/connectors/atlar/task_fetch_transactions.go

# Search for the usage of the cancel function in the atlar/task_fetch_accounts.go file
rg --type go 'cancel\(\)' components/payments/cmd/connectors/internal/connectors/atlar/task_fetch_accounts.go

Length of output: 522

components/payments/cmd/connectors/internal/connectors/fawry/resolve.go (1)

18-21: Consider adding a mutex for fetchTransactionsOnce map access.

The fetchTransactionsOnce map is accessed and modified without explicit synchronization mechanisms. To ensure thread safety, especially in concurrent environments, consider using a mutex to protect access to this map.

  • File: components/payments/cmd/connectors/internal/connectors/mangopay/task_fetch_wallets.go
  • Concern: Access to fetchTransactionsOnce should be synchronized to avoid race conditions.
Analysis chain

Consider thread safety for TaskMemoryState.

The fetchTransactionsOnce map is used for synchronization. Ensure that access to this map is thread-safe, especially if it is modified concurrently.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify thread safety for `fetchTransactionsOnce`.

# Test: Search for concurrent access to `fetchTransactionsOnce`. Expect: Proper synchronization mechanisms.
rg --type go 'fetchTransactionsOnce'

Length of output: 994


Script:

#!/bin/bash
# Check for synchronization mechanisms around access to `fetchTransactionsOnce`.
rg --type go 'fetchTransactionsOnce' -A 5

Length of output: 3276

components/payments/cmd/connectors/internal/connectors/fawry/connector.go (1)

16-32: Ensure idempotency in the Install method.

The Install method does not handle the ErrAlreadyScheduled error, which may lead to duplicate task scheduling. Consider adding error handling for ErrAlreadyScheduled to ensure idempotency and avoid unintended side effects.

  • File: components/payments/cmd/connectors/internal/connectors/fawry/connector.go
  • Lines: 16-32
Analysis chain

Ensure proper task scheduling in Install method.

The Install method schedules a task immediately. Verify that this behavior is intended and that the task is idempotent to avoid unintended side effects.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify task scheduling behavior in `Install` method.

# Test: Search for task scheduling logic. Expect: Idempotent task behavior.
rg --type go 'Schedule'

Length of output: 72554


Script:

#!/bin/bash
# Description: Check for handling of ErrAlreadyScheduled in the `connector.go` file and related logic.

# Test: Search for ErrAlreadyScheduled handling in the relevant files.
rg --type go 'ErrAlreadyScheduled'

Length of output: 8881

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f0df8c3 and 242732a.

Files selected for processing (10)
  • components/payments/cmd/connectors/internal/connectors/fawry/client/client.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/client/invoice.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/client/notification.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/config.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/connector.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/currencies.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/ingest_payment.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/loader.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/resolve.go (1 hunks)
  • components/payments/cmd/connectors/internal/connectors/fawry/webhook.go (1 hunks)
Files skipped from review due to trivial changes (1)
  • components/payments/cmd/connectors/internal/connectors/fawry/ingest_payment.go
Additional comments not posted (9)
components/payments/cmd/connectors/internal/connectors/fawry/currencies.go (1)

1-8: Currency mapping looks good.

The use of the currency.ISO4217Currencies map ensures consistency and accuracy for currency information.

components/payments/cmd/connectors/internal/connectors/fawry/client/invoice.go (1)

3-7: Consider using a consistent date type for ExpiryDate.

The ExpiryDate field is currently defined as uint64, which might not be consistent with the string type used for DueDate. Consider using a consistent date format or type for both fields to avoid confusion and ensure proper handling of date values.

components/payments/cmd/connectors/internal/connectors/fawry/client/client.go (2)

9-18: Request signing implementation is effective.

The SignRequest method securely hashes inputs using SHA-256, which is a good practice for ensuring data integrity.


24-26: Client initialization looks good.

The NewClient function provides a simple way to create a new Client instance with default values.

components/payments/cmd/connectors/internal/connectors/fawry/config.go (2)

5-10: Ensure Secure Handling of Sensitive Information.

The SecureKey field contains sensitive information. Ensure that this key is securely managed and not exposed in logs or error messages.


16-18: Method ConnectorName is Simple and Effective.

The method correctly returns the connector's name, which enhances the readability and usability of the Config struct.

components/payments/cmd/connectors/internal/connectors/fawry/loader.go (2)

26-35: Review Webhook Security in Router Method.

Ensure that the webhook handler in the Router method includes proper authentication and validation to prevent unauthorized access and ensure data integrity.


41-43: Clarify the Purpose of newConnector in Load Method.

The Load method returns a new connector using newConnector(). Ensure that this function initializes the connector with the necessary configurations and resources.

components/payments/cmd/connectors/internal/connectors/fawry/connector.go (1)

67-69: Verify currency support logic.

The SupportedCurrenciesAndDecimals method returns a map of supported currencies. Ensure this map is correctly initialized and updated as needed.

Verification successful

Currency support logic is correctly implemented for the Fawry connector.

The supportedCurrenciesWithDecimal map is correctly initialized with the "EGP" currency, using the ISO 4217 standard for its decimal precision. There are no updates to this map elsewhere in the fawry connector.

  • Initialization found in components/payments/cmd/connectors/internal/connectors/fawry/currencies.go.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify currency support logic.

# Test: Search for initialization and updates to `supportedCurrenciesWithDecimal`. Expect: Correct initialization and updates.
rg --type go 'supportedCurrenciesWithDecimal'

Length of output: 16657


Script:

#!/bin/bash
# Extract the initialization details of `supportedCurrenciesWithDecimal` in the fawry connector.
rg --type go 'supportedCurrenciesWithDecimal = map' -A 10 components/payments/cmd/connectors/internal/connectors/fawry/currencies.go

Length of output: 233

Comment on lines 3 to 13
type Notification struct {
RequestID string `json:"requestId"`
FawryRefNumber string `json:"fawryRefNumber"`
MerchantRefNumber string `json:"merchantRefNumber"`
PaymentAmount string `json:"paymentAmount"`
OrderAmount string `json:"orderAmount"`
FawryFees string `json:"fawryFees"`
OrderStatus string `json:"orderStatus"`
PaymentMethod string `json:"paymentMethod"`
MessageSignature string `json:"messageSignature"`
InvoiceInfo InvoiceInfo `json:"invoiceInfo"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider Using Numeric Types for Monetary Values.

Fields like PaymentAmount, OrderAmount, and FawryFees are defined as strings. Consider using a numeric type or a decimal library to avoid issues with arithmetic operations and ensure precision.

Comment on lines +48 to +50
func NewWebhook() *Webhook {
return &Webhook{}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider dependency injection for NewWebhook.

The NewWebhook function currently returns an empty Webhook. If the Webhook requires dependencies, consider using dependency injection to pass them during initialization.

func NewWebhook(dependencies ...interface{}) *Webhook {
    return &Webhook{
        // Initialize with dependencies
    }
}

Comment on lines +18 to +45
func (w *Webhook) Handle() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
conn := task.ConnectorContextFromContext(r.Context())

b, err := io.ReadAll(r.Body)

n := client.Notification{}
if err := json.Unmarshal(b, &n); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

ctx, _ := contextutil.DetachedWithTimeout(r.Context(), 30*time.Second)
td, err := models.EncodeTaskDescriptor(TaskDescriptor{
Name: TaskIngestPayment,
Key: TaskIngestPayment,
Payload: string(b),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

conn.Scheduler().Schedule(ctx, td, models.TaskSchedulerOptions{
ScheduleOption: models.OPTIONS_RUN_NOW,
RestartOption: models.OPTIONS_RESTART_IF_NOT_ACTIVE,
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Enhance error handling in Handle method.

The method currently returns a 500 error for any JSON parsing issues. Consider using more specific status codes, such as 400 for bad requests, to better inform the client of the nature of the error.

-  http.Error(w, err.Error(), http.StatusInternalServerError)
+  http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (w *Webhook) Handle() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
conn := task.ConnectorContextFromContext(r.Context())
b, err := io.ReadAll(r.Body)
n := client.Notification{}
if err := json.Unmarshal(b, &n); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ctx, _ := contextutil.DetachedWithTimeout(r.Context(), 30*time.Second)
td, err := models.EncodeTaskDescriptor(TaskDescriptor{
Name: TaskIngestPayment,
Key: TaskIngestPayment,
Payload: string(b),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
conn.Scheduler().Schedule(ctx, td, models.TaskSchedulerOptions{
ScheduleOption: models.OPTIONS_RUN_NOW,
RestartOption: models.OPTIONS_RESTART_IF_NOT_ACTIVE,
})
}
func (w *Webhook) Handle() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
conn := task.ConnectorContextFromContext(r.Context())
b, err := io.ReadAll(r.Body)
n := client.Notification{}
if err := json.Unmarshal(b, &n); err != nil {
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
return
}
ctx, _ := contextutil.DetachedWithTimeout(r.Context(), 30*time.Second)
td, err := models.EncodeTaskDescriptor(TaskDescriptor{
Name: TaskIngestPayment,
Key: TaskIngestPayment,
Payload: string(b),
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
conn.Scheduler().Schedule(ctx, td, models.TaskSchedulerOptions{
ScheduleOption: models.OPTIONS_RUN_NOW,
RestartOption: models.OPTIONS_RESTART_IF_NOT_ACTIVE,
})
}

Comment on lines +29 to +59
func Resolve(
logger logging.Logger,
config Config,
taskMemoryState *TaskMemoryState,
) func(taskDefinition TaskDescriptor) task.Task {
client := client.NewClient()
err := client.Init()
if err != nil {
logger.Error(err)
return func(taskDescriptor TaskDescriptor) task.Task {
return func() error {
return fmt.Errorf("cannot build fawry client: %w", err)
}
}
}

return func(taskDescriptor TaskDescriptor) task.Task {
switch taskDescriptor.Key {
case TaskMain:
return func() error {
return nil
}
case TaskIngestPayment:
return taskIngestPayment
default:
return func() error {
return fmt.Errorf("unknown task key: %s", taskDescriptor.Key)
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error handling in Resolve function.

The Resolve function logs an error and returns a task that will always fail if the client initialization fails. Consider implementing a retry mechanism or alerting system to handle such failures more gracefully.

if err != nil {
    logger.Error(err)
    // Implement retry logic or alerting
    return func(taskDescriptor TaskDescriptor) task.Task {
        return func() error {
            return fmt.Errorf("cannot build fawry client: %w", err)
        }
    }
}

Comment on lines +35 to +37
func (c *Connector) Uninstall(ctx task.ConnectorContext) error {
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Implement logic for Uninstall method.

The Uninstall method currently returns nil. If there are resources to clean up or tasks to cancel, implement the necessary logic here.

func (c *Connector) Uninstall(ctx task.ConnectorContext) error {
    // Implement uninstallation logic
    return nil
}

Comment on lines +48 to +64
func (c *Connector) UpdateConfig(ctx task.ConnectorContext, config models.ConnectorConfigObject) error {
panic("not supported")
}

func (c *Connector) InitiatePayment(ctx task.ConnectorContext, transfer *models.TransferInitiation) error {
panic("not supported")
}

// ReversePayment
func (c *Connector) ReversePayment(ctx task.ConnectorContext, reversal *models.TransferReversal) error {
panic("not supported")
}

// CreateExternalBankAccount
func (c *Connector) CreateExternalBankAccount(ctx task.ConnectorContext, account *models.BankAccount) error {
panic("not supported")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Handle unsupported operations gracefully.

The methods UpdateConfig, InitiatePayment, ReversePayment, and CreateExternalBankAccount currently panic. Consider returning a descriptive error instead to avoid disrupting the application flow.

func (c *Connector) UpdateConfig(ctx task.ConnectorContext, config models.ConnectorConfigObject) error {
    return fmt.Errorf("operation not supported")
}

Comment on lines +71 to +73
func newConnector() *Connector {
return &Connector{}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Enhance newConnector function with initialization logic.

The newConnector function currently returns an empty Connector. If there are configurations or dependencies to initialize, consider adding them here.

func newConnector(logger logging.Logger, cfg Config) *Connector {
    return &Connector{
        logger: logger,
        cfg:    cfg,
        // Initialize other fields
    }
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 242732a and 9610bff.

Files selected for processing (1)
  • components/payments/cmd/connectors/internal/connectors/fawry/client/notification.go (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • components/payments/cmd/connectors/internal/connectors/fawry/client/notification.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant