Skip to content

Commit

Permalink
feat: new embed signup process (#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulobernardoaf authored Feb 16, 2024
1 parent edc8f64 commit 0d3d888
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 40 deletions.
21 changes: 15 additions & 6 deletions temba/channels/types/whatsapp_cloud/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,30 @@ def get_urls(self):
def activate(self, channel):
waba_id = channel.config.get("wa_waba_id")
wa_pin = channel.config.get("wa_pin")
wa_user_auth_token = channel.config.get("wa_user_auth_token")

headers = {"Authorization": f"Bearer {settings.WHATSAPP_ADMIN_SYSTEM_USER_TOKEN}"}
weni_headers = {"Authorization": f"Bearer {settings.WHATSAPP_ADMIN_SYSTEM_USER_TOKEN}"}
user_headers = {"Authorization": f"Bearer {wa_user_auth_token}"}

# Subscribe to events
url = f"https://graph.facebook.com/v13.0/{waba_id}/subscribed_apps"
resp = requests.post(url, headers=headers)
url = f"https://graph.facebook.com/v18.0/{waba_id}/subscribed_apps"
resp = requests.post(url, headers=user_headers)

if resp.status_code != 200: # pragma: no cover
raise ValidationError(_("Unable to subscribe to app to WABA with ID %s" % waba_id))

# register numbers
url = f"https://graph.facebook.com/v13.0/{channel.address}/register"
url = f"https://graph.facebook.com/v18.0/{settings.WHATSAPP_CLOUD_EXTENDED_CREDIT_ID}/whatsapp_credit_sharing_and_attach"
params = dict(waba_id=waba_id, waba_currency="USD")
resp = requests.post(url, params=params, headers=weni_headers)

if resp.status_code != 200: # pragma: no cover
raise ValidationError(_("Unable to share credit line to WABA with ID %s" % waba_id))

# # register numbers
url = f"https://graph.facebook.com/v18.0/{channel.address}/register"
data = {"messaging_product": "whatsapp", "pin": wa_pin}

resp = requests.post(url, data=data, headers=headers)
resp = requests.post(url, data=data, headers=weni_headers)

if resp.status_code != 200: # pragma: no cover
raise ValidationError(
Expand Down
20 changes: 13 additions & 7 deletions temba/channels/types/whatsapp_cloud/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def pre_process(self, request, *args, **kwargs):
app_id = settings.WHATSAPP_APPLICATION_ID
app_secret = settings.WHATSAPP_APPLICATION_SECRET

url = "https://graph.facebook.com/v13.0/debug_token"
url = "https://graph.facebook.com/v18.0/debug_token"
params = {"access_token": f"{app_id}|{app_secret}", "input_token": oauth_user_token}

response = requests.get(url, params=params)
Expand All @@ -65,7 +65,7 @@ def get_context_data(self, **kwargs):
app_id = settings.WHATSAPP_APPLICATION_ID
app_secret = settings.WHATSAPP_APPLICATION_SECRET

url = "https://graph.facebook.com/v13.0/debug_token"
url = "https://graph.facebook.com/v18.0/debug_token"
params = {"access_token": f"{app_id}|{app_secret}", "input_token": oauth_user_token}

response = requests.get(url, params=params)
Expand All @@ -90,7 +90,7 @@ def get_context_data(self, **kwargs):

seen_waba.append(target_waba)

url = f"https://graph.facebook.com/v13.0/{target_waba}"
url = f"https://graph.facebook.com/v18.0/{target_waba}"
params = {
"access_token": oauth_user_token,
"fields": "id,name,currency,message_template_namespace,owner_business_info,account_review_status,on_behalf_of_business_info,primary_funding_id,purchase_order_number,timezone_id",
Expand All @@ -102,7 +102,7 @@ def get_context_data(self, **kwargs):

business_id = target_waba_details["on_behalf_of_business_info"]["id"]

url = f"https://graph.facebook.com/v13.0/{target_waba}/phone_numbers"
url = f"https://graph.facebook.com/v18.0/{target_waba}/phone_numbers"
params = {"access_token": oauth_user_token}
response = requests.get(url, params=params)
response_json = response.json()
Expand Down Expand Up @@ -140,6 +140,7 @@ def get_context_data(self, **kwargs):
return context

def form_valid(self, form):
user_auth = self.request.session.get(Channel.CONFIG_WHATSAPP_CLOUD_USER_TOKEN, None)
org = self.request.org

number = form.cleaned_data["number"]
Expand All @@ -161,6 +162,7 @@ def form_valid(self, form):
"wa_business_id": business_id,
"wa_message_template_namespace": message_template_namespace,
"wa_pin": pin,
"wa_user_auth_token": user_auth,
}

# don't add the same number twice to the same account
Expand All @@ -184,9 +186,13 @@ def form_valid(self, form):
return self.form_invalid(form)

# assign system user to WABA
url = f"https://graph.facebook.com/v13.0/{waba_id}/assigned_users"
params = {"user": f"{settings.WHATSAPP_ADMIN_SYSTEM_USER_ID}", "tasks": ["MANAGE"]}
headers = {"Authorization": f"Bearer {settings.WHATSAPP_ADMIN_SYSTEM_USER_TOKEN}"}
url = f"https://graph.facebook.com/v18.0/{waba_id}/assigned_users"
params = {
"user": f"{settings.WHATSAPP_ADMIN_SYSTEM_USER_ID}",
"access_token": {settings.WHATSAPP_ADMIN_SYSTEM_USER_TOKEN},
"tasks": ["MANAGE"],
}
headers = {"Authorization": f"Bearer {user_auth}"}

resp = requests.post(url, params=params, headers=headers)

Expand Down
27 changes: 23 additions & 4 deletions temba/orgs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1748,18 +1748,36 @@ def get_context_data(self, **kwargs):

class WhatsappCloudConnect(InferOrgMixin, OrgPermsMixin, SmartFormView):
class WhatsappCloudConnectForm(forms.Form):
user_access_token = forms.CharField(min_length=32, required=True)
user_access_code = forms.CharField(min_length=32, required=True)
user_auth_token = forms.CharField(min_length=32, required=False)

def clean(self):
try:
auth_token = self.cleaned_data.get("user_access_token", None)
access_code = self.cleaned_data.get("user_access_code", None)

app_id = settings.WHATSAPP_APPLICATION_ID
app_secret = settings.WHATSAPP_APPLICATION_SECRET

url = "https://graph.facebook.com/v13.0/debug_token"
# Exchange user auth code for a permanent token
url = "https://graph.facebook.com/v18.0/oauth/access_token"
params = dict(
client_id=app_id,
client_secret=app_secret,
code=access_code,
)

response = requests.get(url, params=params)
if response.status_code != 200:
raise Exception("Failed to exchange user auth code")

auth_token = response.json().get("access_token")
params = {"access_token": f"{app_id}|{app_secret}", "input_token": auth_token}

# Save auth token in form
self.cleaned_data["user_auth_token"] = auth_token

# Debug user auth_token to check if we have required permissions
url = "https://graph.facebook.com/v18.0/debug_token"
response = requests.get(url, params=params)
if response.status_code != 200: # pragma: no cover
raise Exception("Failed to debug user token")
Expand Down Expand Up @@ -1790,7 +1808,7 @@ def pre_process(self, request, *args, **kwargs):
return super().pre_process(request, *args, **kwargs)

def form_valid(self, form):
auth_token = form.cleaned_data["user_access_token"]
auth_token = form.cleaned_data["user_auth_token"]

# add the credentials to the session
self.request.session[Channel.CONFIG_WHATSAPP_CLOUD_USER_TOKEN] = auth_token
Expand All @@ -1800,6 +1818,7 @@ def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["connect_url"] = reverse("orgs.org_whatsapp_cloud_connect")
context["whatsapp_app_id"] = settings.WHATSAPP_APPLICATION_ID
context["whatsapp_config_id"] = settings.WHATSAPP_CONFIGURATION_ID

claim_error = None
if context["form"].errors:
Expand Down
2 changes: 2 additions & 0 deletions temba/settings_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,8 @@
WHATSAPP_APPLICATION_ID = os.environ.get("WHATSAPP_APPLICATION_ID", "")
WHATSAPP_APPLICATION_SECRET = os.environ.get("WHATSAPP_APPLICATION_SECRET", "")
WHATSAPP_WEBHOOK_SECRET = os.environ.get("WHATSAPP_WEBHOOK_SECRET", "")
WHATSAPP_CONFIGURATION_ID = os.environ.get("WHATSAPP_CONFIGURATION_ID", "")
WHATSAPP_CLOUD_EXTENDED_CREDIT_ID = os.environ.get("WHATSAPP_CLOUD_EXTENDED_CREDIT_ID", "")

# -----------------------------------------------------------------------------------
# IP Addresses
Expand Down
63 changes: 40 additions & 23 deletions templates/orgs/org_whatsapp_cloud_connect.haml
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@
-trans "Approve the permissions, these are required for us to access the API on your behalf."

#fb-app-connect.flex.mt-4
.button-primary(onclick="launchWhatsAppSignup()")
#fb-app-connect-button.button-primary
-trans "Add Facebook Business"

%form#claim-form(style="display:none;" method="POST" action="{{ connect_url }}")
{% csrf_token %}
%input#user-access-token(type="text" name="user_access_token")
%input#user-access-code(type="text" name="user_access_code")

-block extra-script
{{ block.super }}
:javascript

let fbq = null;

$(document).ready(function(){
var hash = window.location.hash.substring(1)
var result = hash.split('&').reduce(function (res, item) {
Expand All @@ -47,19 +50,15 @@

var accessToken = result.long_lived_token || result.access_token;
if (accessToken) {
$("#user-access-token").val(accessToken);
$("#user-access-code").val(accessToken);
$("#claim-form").submit();
}
});

window.fbAsyncInit = function () {
// JavaScript SDK configuration and setup
FB.init({
appId: '{{ whatsapp_app_id }}', // Meta App ID
xfbml: true, // parse social plugins on this page
version: 'v14.0' //Graph API version
});
};
// add launchWhatsAppSignup to the button
$("#fb-app-connect-button").click(function() {
launchWhatsAppSignup();
});
});

// Load the JavaScript SDK asynchronously
(function (d, s, id) {
Expand All @@ -70,21 +69,39 @@
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

window.fbAsyncInit = function () {
// JavaScript SDK configuration and setup
FB.init({
appId: '{{ whatsapp_app_id }}', // Meta App ID
xfbml: true, // parse social plugins on this page
version: 'v18.0', //Graph API version,
status: true
});
};

function loginCallback(response) {
if (response.authResponse) {
const code = response.authResponse.code;
if (code) {
$("#user-access-code").val(code);
$("#claim-form").submit();
}
} else {
console.log('User cancelled login or did not fully authorize.');
}
}

// Facebook Login with JavaScript SDK
function launchWhatsAppSignup() {
// Launch Facebook login
FB.login(function (response) {
if (response.authResponse) {
const accessToken = response.authResponse.accessToken;
if (accessToken) {
$("#user-access-token").val(accessToken);
$("#claim-form").submit();
}
}
}, {
scope: 'business_management,whatsapp_business_management,whatsapp_business_messaging',
FB.login(loginCallback, {
config_id: '{{ whatsapp_config_id }}', // configuration ID goes here
response_type: 'code', // must be set to 'code' for System User access token
override_default_response_type: true, // when true, any response types passed in the "response_type" will take precedence over the default types
extras: {
feature: 'whatsapp_embedded_signup',
sessionInfoVersion: 2, // Receive Session Logging Info
featureType: "only_waba_sharing" // Bypass phone number selection
}
});

}

0 comments on commit 0d3d888

Please sign in to comment.