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: new embed signup process #466

Merged
merged 1 commit into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
});

}
Loading