add fields to capture venmo details

* add new field: venmo_user_id
* add new field: phone_last_4

changed the forms to replace paypal with venmo
This commit is contained in:
Nuwan 2025-01-18 19:14:28 +05:30
parent 51f1a7e58e
commit 28487f271e
7 changed files with 99 additions and 22 deletions

View File

@ -4,7 +4,7 @@ import { Card, CardBody, Row, Col } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader'; import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import JKAffiliatePayeeAddress from './JKAffiliatePayeeAddress'; import JKAffiliatePayeeAddress from './JKAffiliatePayeeAddress';
import JKAffiliatePayeePaypal from './JKAffiliatePayeePaypal'; import JKAffiliatePayeeVenmo from './JKAffiliatePayeeVenmo';
import JKAffiliatePayeeTax from './JKAffiliatePayeeTax'; import JKAffiliatePayeeTax from './JKAffiliatePayeeTax';
import { useAuth } from '../../context/UserAuth'; import { useAuth } from '../../context/UserAuth';
import { getAffiliatePartnerData } from '../../helpers/rest'; import { getAffiliatePartnerData } from '../../helpers/rest';
@ -87,7 +87,7 @@ const JKAffiliatePayee = () => {
<JKAffiliatePayeeAddress affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} /> <JKAffiliatePayeeAddress affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} />
</Col> </Col>
<Col> <Col>
<JKAffiliatePayeePaypal affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} /> <JKAffiliatePayeeVenmo affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} />
<div className='mb-2' /> <div className='mb-2' />
<JKAffiliatePayeeTax affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} /> <JKAffiliatePayeeTax affiliateUser={affiliateUser} onSubmit={onSubmit} submitting={submitting} />
</Col> </Col>

View File

@ -14,6 +14,7 @@ const JKAffiliatePayeeAddress = ({ affiliateUser, onSubmit, submitting }) => {
setValue setValue
} = useForm({ } = useForm({
defaultValues: { defaultValues: {
phone_last_4: '',
address1: '', address1: '',
address2: '', address2: '',
city: '', city: '',
@ -24,6 +25,7 @@ const JKAffiliatePayeeAddress = ({ affiliateUser, onSubmit, submitting }) => {
}); });
useEffect(() => { useEffect(() => {
setValue('phone_last_4', affiliateUser?.address?.phone_last_4 || '');
setValue('address1', affiliateUser?.address?.address1 || ''); setValue('address1', affiliateUser?.address?.address1 || '');
setValue('address2', affiliateUser?.address?.address2 || ''); setValue('address2', affiliateUser?.address?.address2 || '');
setValue('city', affiliateUser?.address?.city || ''); setValue('city', affiliateUser?.address?.city || '');
@ -47,6 +49,30 @@ const JKAffiliatePayeeAddress = ({ affiliateUser, onSubmit, submitting }) => {
{t('payee.address.help_text')} {t('payee.address.help_text')}
</small> </small>
<Form noValidate className="mt-3" onSubmit={handleSubmit(onSubmitAddress)}> <Form noValidate className="mt-3" onSubmit={handleSubmit(onSubmitAddress)}>
<FormGroup>
<Label for="phone_last_4">{t('payee.address.form.phone_last_4')}</Label>
<Controller
name="phone_last_4"
control={control}
rules={{
required: t('payee.address.form.validations.phone_last_4.required')
}}
render={({ field }) => (
<Input
{...field}
type="text"
className="form-control"
id="phone_last_4"
maxLength={4}
/>
)}
/>
{errors.phone_last_4 && (
<div className="text-danger">
<small>{errors.phone_last_4.message}</small>
</div>
)}
</FormGroup>
<FormGroup> <FormGroup>
<Label for="address1">{t('payee.address.form.address1')}</Label> <Label for="address1">{t('payee.address.form.address1')}</Label>
<Controller <Controller

View File

@ -3,8 +3,9 @@ import { Card, CardBody, CardHeader, Form, FormGroup, Label, Input } from 'react
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import PropTypes from 'prop-types';
const JKAffiliatePayeePaypal = ({ affiliateUser, onSubmit, submitting }) => { const JKAffiliatePayeeVenmo = ({ affiliateUser, onSubmit, submitting }) => {
const { t } = useTranslation('affiliate'); const { t } = useTranslation('affiliate');
const { const {
handleSubmit, handleSubmit,
@ -14,45 +15,45 @@ const JKAffiliatePayeePaypal = ({ affiliateUser, onSubmit, submitting }) => {
setValue setValue
} = useForm({ } = useForm({
defaultValues: { defaultValues: {
paypal_id: '' venmo_user_id: ''
} }
}); });
useEffect(() => { useEffect(() => {
setValue('paypal_id', affiliateUser?.paypal_id || ''); setValue('venmo_user_id', affiliateUser?.venmo_user_id || '');
}, [affiliateUser]); }, [affiliateUser]);
const onSubmitPaypal = data => { const onSubmitVenmo = data => {
const params = { "paypal_id": data.paypal_id }; const params = { "venmo_user_id": data.venmo_user_id };
onSubmit(params); onSubmit(params);
} }
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
<h5>{t('payee.paypal.title')}</h5> <h5>{t('payee.venmo.title')}</h5>
</CardHeader> </CardHeader>
<CardBody className="bg-light" style={{ minHeight: 200 }}> <CardBody className="bg-light" style={{ minHeight: 200 }}>
<small>{t('payee.paypal.help_text')}</small> <small>{t('payee.venmo.help_text')}</small>
<Form noValidate className="mt-3" onSubmit={handleSubmit(onSubmitPaypal)}> <Form noValidate className="mt-3" onSubmit={handleSubmit(onSubmitVenmo)}>
<FormGroup> <FormGroup>
<Label for="paypal_id">{t('payee.paypal.form.paypal_id')}</Label> <Label for="venmo_user_id">{t('payee.venmo.form.venmo_user_id')}</Label>
<Controller <Controller
name="paypal_id" name="venmo_user_id"
control={control} control={control}
rules={{ rules={{
required: t('payee.paypal.form.validations.paypal_id.required') required: t('payee.venmo.form.validations.venmo_user_id.required')
}} }}
render={({ field }) => <Input {...field} type="text" className="form-control" id="paypal_id" />} render={({ field }) => <Input {...field} type="text" className="form-control" id="venmo_user_id" />}
/> />
{errors.paypal_id && ( {errors.venmo_user_id && (
<div className="text-danger"> <div className="text-danger">
<small>{errors.paypal_id.message}</small> <small>{errors.venmo_user_id.message}</small>
</div> </div>
)} )}
</FormGroup> </FormGroup>
<div className='d-flex align-content-center justify-content-end'> <div className='d-flex align-content-center justify-content-end'>
<input type="submit" formNoValidate className="btn btn-primary" value={t('payee.paypal.form.submit')} disabled={submitting} data-testid="paypal_submit" /> <input type="submit" formNoValidate className="btn btn-primary" value={t('payee.venmo.form.submit')} disabled={submitting} data-testid="paypal_submit" />
<span className='ml-2'> <span className='ml-2'>
{ submitting && <FontAwesomeIcon icon="spinner" />} { submitting && <FontAwesomeIcon icon="spinner" />}
</span> </span>
@ -63,4 +64,15 @@ const JKAffiliatePayeePaypal = ({ affiliateUser, onSubmit, submitting }) => {
); );
}; };
export default JKAffiliatePayeePaypal; JKAffiliatePayeeVenmo.propTypes = {
affiliateUser: PropTypes.object,
onSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
};
JKAffiliatePayeeVenmo.defaultProps = {
onSubmit: () => {},
submitting: false
};
export default JKAffiliatePayeeVenmo;

View File

@ -14,9 +14,10 @@
"not_affiliate": "You are not currently a JamKazam affiliate.", "not_affiliate": "You are not currently a JamKazam affiliate.",
"learn_to_earn": "Learn how you can earn cash simply by telling your friends and followers about JamKazam.", "learn_to_earn": "Learn how you can earn cash simply by telling your friends and followers about JamKazam.",
"address": { "address": {
"title": "Address", "title": "Address & Phone",
"help_text": "If you are a US resident, you must provide your home mailing address for JamKazam to process affiliate payments, per US tax regulations. Please enter your address information below.", "help_text": "We need the last 4 digits of your phone number to process Venmo payments for all affiliates. In addition, for affiliates that are US residents, you must provide either your home mailing address (for individuals) or your business mailing address (for affiliates operating as a business) for JamKazam to process affiliate payments, per US tax regulations. Please enter your phone and address information below.",
"form": { "form": {
"phone_last_4": "Last 4 Digits of Phone Number",
"address1": "Street Address 1", "address1": "Street Address 1",
"address2": "Street Address 2", "address2": "Street Address 2",
"city": "City", "city": "City",
@ -24,6 +25,10 @@
"zip": "Zip Code", "zip": "Zip Code",
"submit": "Save", "submit": "Save",
"validations": { "validations": {
"phone_last_4": {
"required": "Last 4 Digits of Phone Number is required",
"invalid": "Last 4 Digits of Phone Number is invalid"
},
"address1": "Street Address 1 is required", "address1": "Street Address 1 is required",
"city": "City is required", "city": "City is required",
"state": "State is required", "state": "State is required",
@ -42,6 +47,20 @@
} }
} }
}, },
"venmo": {
"title": "Venmo",
"help_text": "JamKazam makes affiliate payments via Venmo. Please enter your Venmo user ID below - e.g. @username.",
"form": {
"venmo_user_id": "Venmo User ID",
"submit": "Save",
"validations": {
"venmo_user_id": {
"required": "Venmo User ID is required",
"invalid": "Venmo User ID is invalid"
}
}
}
},
"tax": { "tax": {
"title": "Tax", "title": "Tax",
"help_text": "If you are a US resident, you must provide your tax ID for JamKazam to process affiliate payments, per US tax regulations. Please enter your tax ID information below. If you are not a US resident, you may leave this field blank.", "help_text": "If you are a US resident, you must provide your tax ID for JamKazam to process affiliate payments, per US tax regulations. Please enter your tax ID information below. If you are not a US resident, you may leave this field blank.",

View File

@ -0,0 +1,8 @@
class AddVenmoUserIdToAffiliatePartners < ActiveRecord::Migration
def self.up
execute("ALTER TABLE public.affiliate_partners ADD COLUMN venmo_user_id VARCHAR(255);")
end
def self.down
execute("ALTER TABLE public.affiliate_partners DROP COLUMN venmo_user_id;")
end
end

View File

@ -0,0 +1,8 @@
class AddPhoneLast4ToAffiliatePartners < ActiveRecord::Migration
def self.up
execute "ALTER TABLE affiliate_partners ADD COLUMN phone_last_4 VARCHAR(4)"
end
def self.down
execute "ALTER TABLE affiliate_partners DROP COLUMN phone_last_4"
end
end

View File

@ -981,7 +981,9 @@ class ApiUsersController < ApiController
if request.post? if request.post?
oo.address = params[:address] oo.address = params[:address]
oo.tax_identifier = params[:tax_identifier] oo.tax_identifier = params[:tax_identifier]
oo.paypal_id = params[:paypal_id] #oo.paypal_id = params[:paypal_id]
oo.venmo_user_id = params[:venmo_user_id]
oo.phone_last_4 = params[:phone_last_4]
oo.save! oo.save!
render json: {}, status: 200 render json: {}, status: 200
@ -990,7 +992,9 @@ class ApiUsersController < ApiController
result['account'] = { result['account'] = {
'address' => oo.address.clone, 'address' => oo.address.clone,
'tax_identifier' => oo.tax_identifier, 'tax_identifier' => oo.tax_identifier,
'paypal_id' => oo.paypal_id, #'paypal_id' => oo.paypal_id,
'venmo_user_id' => oo.venmo_user_id,
'phone_last_4' => oo.phone_last_4,
'entity_type' => oo.entity_type, 'entity_type' => oo.entity_type,
'partner_name' => oo.partner_name, 'partner_name' => oo.partner_name,
'partner_id' => oo.partner_user_id, 'partner_id' => oo.partner_user_id,