-
Notifications
You must be signed in to change notification settings - Fork 0
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
Capture Payment Phone numbers #50
base: main
Are you sure you want to change the base?
Conversation
f0ac6d2
to
bb30069
Compare
Corresponding connect PR dimagi/commcare-connect#428 |
@calellowitz Please have a review of this and the corresponding connect PR that's linked. It will make it easy for mobile to test these. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few questions and a few suggestions
telecom_provider = models.CharField(max_length=50, blank=True, null=True) | ||
# whether the number is verified using OTP | ||
is_verified = models.BooleanField(default=False) | ||
status = models.CharField( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does this column do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tracks whether this is pending for review or approved or rejected by connect user.
from django.conf import settings | ||
|
||
# Create the client instance only once | ||
twilio_client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason to have this be a singleton?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you instantiate the client, there's bunch of auth overhead API calls. Singleton would avoid that overhead each time we need it
try: | ||
phone_info = client.lookups.v1.phone_numbers(phone_number).fetch(type="carrier") | ||
return phone_info.carrier.get("name") | ||
except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't catch bare exceptions. Is there a specific error you are looking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true, I will adjust it such it logs the exception if any. For now, I will catch all exceptions.
'status': PaymentProfile.PENDING | ||
} | ||
) | ||
return PhoneDevice.send_otp_httpresponse(phone_number=payment_profile.phone_number, user=payment_profile.user) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to send this every time they hit here or only on initial creation (and maybe if the phone number changes)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have this single view to manage resend func, so we want to send it each time this view is called.
from users.models import ConnectUser | ||
|
||
|
||
class PaymentProfile(models.Model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there can only be one of these per user, what is the reason to have a separate model rather than adding these columns to the existing model (and then avoiding the extra lookups and possible exceptions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are too many fields to add on user directly (telecom_provider, otp-verification status, payment validation status).
return JsonResponse({"success": True}) | ||
|
||
|
||
class FetchPhoneNumbers(APIView): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: these views should include the "impossible" scopes to prevent access tokens from working. I think it isn't necessary but am not sure enough to avoid adding it for safety.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by "impossible" scopes?
phone_number = data["phone_number"] | ||
status = data["status"] | ||
|
||
filter_conditions |= Q(user__username=username, phone_number=phone_number) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since payment profiles are one to one with username why does the phone number need to be in the query? Can't it just be user__usernames__in=<list from request>
, and similarly the dict can key just off the username?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I guess, we could simplify it. Though, one (debatable) advantage of this is that it would guard against the hypothetical scenario of two users using same payment phone number.
|
||
profiles = PaymentProfile.objects.filter(filter_conditions).select_related("user") | ||
if len(profiles) != len(users_data): | ||
return Response(status=drf_status.HTTP_404_NOT_FOUND) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This throws a 404 if any aren't found? It might be nicer to just return the success list at the end so one bad entry doesn't ruin the whole request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this shouldn't happen since the request is coming from the view which returns valid profiles in the first place. So, it's not worth partially succeeding and then managing partial failure that calls this view.
payments/views.py
Outdated
} | ||
with transaction.atomic(): | ||
for profile in profiles: | ||
key = (profile.user.username, profile.phone_number) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question about just using username
payments/views.py
Outdated
"approved": [], | ||
"rejected": [], | ||
} | ||
with transaction.atomic(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this guarding against? It only wraps the update at the end, so I am not sure how it could end up rolled back in an unfinished state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I had it when I did it sequentially instead of bulk update, I will remove it.
This implements connect-id side endpoints for capturing users' mobile payment phone numbers. I have tested these locally using curl
Spec