Skip to content

Feat/welcome-email-after-signup #472

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

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

Conversation

sundayonah
Copy link
Collaborator

Description

This pull request implements the welcome email feature for the Paycrest aggregator, enabling a welcome email to be sent to users immediately after signup, before the verification email (in production environments). The feature enhances user onboarding by providing immediate confirmation of account creation with account-type-specific content based on the user’s selected scopes (sender, provider, or both).

References

Closes #471

Testing

Unit and Integration Tests

Checklist

  • I have added documentation and tests for new/changed functionality in this PR
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not main

By submitting a PR, I agree to Paycrest's Contributor Code of Conduct and Contribution Guide.

@sundayonah sundayonah requested review from chibie and 5ran6 as code owners May 15, 2025 14:14
…ions

- Added new webhook endpoints for processing Google Forms submissions and Slack interactions.
- Enhanced the NotificationConfiguration struct to include VerificationLink and SlackBotToken.
- Implemented email services for sending KYB approval and rejection notifications.
- Integrated Slack notifications for new KYB submissions and action feedback.
- Updated routes to include new webhook functionalities.
- Implemented mechanisms to track and prevent duplicate processing of approve and reject actions in Slack interactions.
- Introduced a mutex for safe concurrent access to the processed actions map.
- Enhanced the SlackInteractionHandler to respond appropriately when actions are already processed.
…unctionality

- Implemented KYBFormSubmission schema with fields for email, company name, business addresses, and policy URLs.
- Added BeneficialOwner schema with relevant fields including ownership percentage and identification URLs.
- Created update builders for KYBFormSubmission to handle updates to its fields and relationships.
- Established edges between KYBFormSubmission and BeneficialOwner, allowing for cascading deletes.
Copy link
Contributor

@chibie chibie left a comment

Choose a reason for hiding this comment

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

  • write migration to check if user's scope contains "provider", and move its existing kyb info to the new kyb schema for the user
  • remove KYB fields from providerprofile schema

return []ent.Field{
field.UUID("id", uuid.UUID{}).
Default(uuid.New),
field.String("email"),
Copy link
Contributor

Choose a reason for hiding this comment

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

not necessary... fetch email from user schema

Copy link
Contributor

Choose a reason for hiding this comment

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

replace it with mobile number

field.String("proof_of_residential_address_url"),
field.String("aml_policy_url").
Optional().
Nillable(),
Copy link
Contributor

Choose a reason for hiding this comment

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

remove nillable

BeneficialOwner should be in a separate schema file

KYBFormSubmission should be KYBProfile

Copy link
Contributor

Choose a reason for hiding this comment

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

also add a boolean field isApproved

Comment on lines +81 to +83
edge.To("user", User.Type).
Unique().
Annotations(entsql.OnDelete(entsql.Cascade)),
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be from... and shouldn't be cascading deletion

refer: https://entgo.io/docs/schema-edges#o2o-two-types

@@ -40,7 +40,7 @@ func (User) Fields() []ent.Field {
field.Bool("is_email_verified").
Default(false),
field.Bool("has_early_access"). // has_early_access is "false" by default
Default(false),
Default(false),
Copy link
Contributor

Choose a reason for hiding this comment

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

remove unnecessary indentation

v1.POST("slack-interaction", ctrl.SlackInteractionHandler)

// KYB route
v1.POST("kyb-submission", ctrl.SubmitKYBForm)
Copy link
Contributor

Choose a reason for hiding this comment

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

controller name should be HandleKYBSubmission

logger.Errorf("Failed to send KYB rejection email to %s: %v, response: %+v", email, err, resp)
return resp, fmt.Errorf("failed to send rejection email: %v", err)
}
logger.Infof("KYB rejection email sent to %s, message ID: %s, response: %+v", email, resp.Id, resp)
Copy link
Contributor

Choose a reason for hiding this comment

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

remove log

Comment on lines +245 to +268
resp, err := SendTemplateEmail(payload, "d-b425f024e6554c5ba2b4d03ab0a8b25d")
if err != nil {
logger.Errorf("Failed to send welcome email to %s: %v", email, err)
} else {
logger.Infof("Welcome email sent successfully.")
}
return resp, err
}

// SendKYBApprovalEmail sends a KYB approval email.
func (m *EmailService) SendKYBApprovalEmail(email, firstName string) (types.SendEmailResponse, error) {
payload := types.SendEmailPayload{
FromAddress: _DefaultFromAddress,
ToAddress: email,
DynamicData: map[string]interface{}{
"first_name": firstName,
},
}
resp, err := SendTemplateEmail(payload, "d-5ebb862274214ba79eae226c09300aa7")
if err != nil {
logger.Errorf("Failed to send KYB approval email to %s: %v", email, err)
} else {
logger.Infof("KYB approval email sent to %s, message ID: %s", email, resp.Id)
}
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 logs with logger.WithFields

refer other parts of codebase

v1.POST("slack-interaction", ctrl.SlackInteractionHandler)

// KYB route
v1.POST("kyb-submission", ctrl.SubmitKYBForm)
Copy link
Contributor

Choose a reason for hiding this comment

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

this endpoint should be authenticated just like profile/register endpoints


// KYBFormSubmissionInput represents the input structure for KYB form submission
type KYBFormSubmissionInput struct {
Email string `json:"email" binding:"required,email"`
Copy link
Contributor

Choose a reason for hiding this comment

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

remove this field... already in user object

Comment on lines +17 to +30
func (BeneficialOwner) Fields() []ent.Field {
return []ent.Field{
field.UUID("id", uuid.UUID{}).
Default(uuid.New),
field.String("full_name").
MaxLen(160),
field.String("residential_address"),
field.String("proof_of_residential_address_url"),
field.String("government_issued_id_url"),
field.String("date_of_birth"),
field.Float("ownership_percentage"),
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

include id document type

field.Enum("government_issued_id_type").
	Values("passport", "drivers_license", "national_id").
	Optional(),

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.

Send Welcome Email After User Signup
2 participants