UNPKG

@proca/widget

Version:

Proca is an open-source campaign toolkit designed to empower activists and organisations in their digital advocacy efforts. It provides a flexible and customisable platform for creating and managing online petitions, email campaigns, and other forms of di

522 lines (486 loc) 14.1 kB
async function graphQL(operation, query, options) { if (!options) options = {}; if (!options.apiUrl) options.apiUrl = process.env.REACT_APP_API_URL || "https://api.proca.app/api"; let data = null; const headers = { "Content-Type": "application/json", Accept: "application/json", }; if (options.authorization) { // var auth = 'Basic ' + Buffer.from(options.authorization.username + ':' + options.authorization.username.password).toString('base64'); headers.Authorization = `Basic ${options.authorization}`; } // console.debug("graphql: ", query, options.variables); await fetch( options.apiUrl + (options.variables.actionPage ? `?id=${options.variables.actionPage}` : ""), { method: "POST", referrerPolicy: "no-referrer-when-downgrade", headers: headers, body: JSON.stringify({ query: query, variables: options.variables, operationName: operation || "", extensions: options.extensions, }), } ) .then(res => { if (!res.ok) { return { errors: [ { message: res.statusText, code: "http_error", status: res.status }, ], }; } return res.json(); }) .then(response => { if (response.errors) { const toCamel = s => s.replace(/([_][a-z])/gi, $1 => $1.toUpperCase().replace("_", "")); response.errors.fields = []; response.errors.forEach(error => { const field = error.path && error.path.slice(-1)[0]; if (!field) return; let msg = error.message.split(":"); if (msg.length === 2) { msg = msg[1]; } else { msg = error.message; } response.errors.fields.push({ name: toCamel(field), message: msg, // error.message, }); }); data = response; return; } data = response.data; }) .catch(error => { console.log(error); data = { errors: [{ code: "network", message: error }] }; return; }); return data; } async function getLatest(actionPage, actionType, options) { var query = `query getLatest($actionPage:Int!,$actionType:String!,limit: Int) { actionPage(id:$actionPage) { campaign { actions(actionType:$actionType, limit: $limit) { list { actionId, fields { key, value } } } } } }`; const variables = { actionPage: actionPage, actionType: actionType || "openletter", limit: options.limit || 1000, }; const response = await graphQL("getLatest", query, { variables: variables, }); if (response.errors) return response; const l = response.actionPage.campaign.actions.list || []; const result = []; l.forEach(d => { const org = { id: d.actionId }; d.fields.forEach(f => { org[f.key] = f.value; }); result.push(org); }); return result.filter( (v, i, a) => a.findIndex(t => t.twitter === v.twitter) === i ); } async function getCount(actionPage, options) { let url = null; //actionCount {actionType, count} var query = `query getCount($actionPage: Int!) {actionPage(id:$actionPage) { campaign { stats { supporterCount } } }} `; query = query.replace(/(\n)/gm, "").replace(/\s\s+/g, " "); if (options?.apiUrl) { url = `${options.apiUrl}?query=${encodeURIComponent(query)}&variables=${encodeURIComponent(`{"actionPage":${Number(actionPage)}}`)}`; } else { url = `${process.env.REACT_APP_API_URL || "https://api.proca.app/api"}?query=${encodeURIComponent(query)}&variables=${encodeURIComponent(`{"actionPage":${Number(actionPage)}}`)}`; } var data = null; await fetch(url) .then(res => { if (!res.ok) { return { errors: [ { message: res.statusText, code: "http_error", status: res.status }, ], }; } return res.json(); }) .then(response => { if (response.errors) { response.errors.forEach(error => console.log(error.message)); data = response; return; } data = response.data; }) .catch(error => { console.log(error); data = { errors: [error], code: "http_error" }; return; }); // const data = await graphQL ("getCount",query,{variables:{ actionPage: Number(actionPage) }}); if (!data || data.errors) return null; /* let count=0; actionType = actionType || "petition"; data.actionPage.campaign.stats.actionCount.forEach(d => { if (d.actionType === actionType) count=d.count; }); return count; */ return data.actionPage.campaign.stats.supporterCount; } async function getCountByName(name) { var query = `query getCountByName($name:String) {actionPage(name:$name){id,campaign{name,title, externalId,stats{supporterCount }}}} `; const response = await graphQL("getCountByName", query, { variables: { name: name }, }); if (!response || response.errors) return response; return { total: response.actionPage.campaign.stats.supporterCount, actionPage: response.actionPage.id, }; } async function addAction(actionPage, actionType, data, test) { var query = `mutation addAction ( $contact: ID!, $actionPage: Int!, $actionType: String!, $payload: Json, $testing: Boolean, $tracking: TrackingInput) { addAction (actionPageId: $actionPage, action: { actionType: $actionType, customFields: $payload, testing: $testing} contactRef: $contact, tracking: $tracking) {contactRef} }`; const variables = { actionPage: actionPage, actionType: actionType, payload: data.payload, contact: data.uuid, testing: !!test, }; if (typeof data.payload === "object") { variables.payload = JSON.stringify(variables.payload); } if (data.tracking && Object.keys(data.tracking).length) { variables.tracking = data.tracking; } const response = await graphQL("addAction", query, { variables: variables, }); return response; } const PROCA_FREQUENCIES = { daily: "DAY", day: "DAILY", month: "MONTHLY", monthly: "MONTHLY", oneoff: "ONE_OFF", week: "WEEKLY", weekly: "WEEKLY", year: "YEARLY", yearly: "YEARLY", }; async function addDonateContact(provider, actionPage, data, test) { delete data.IBAN; if (!data.donation.payload) data.donation.payload = {}; data.donation.payload.provider = provider; data.donation.payload = JSON.stringify(data.donation.payload); if (!Number.isInteger(data.donation.amount)) { throw Error( `Donation amount should be an integer, expressing the amount in cents. You sent '${data.donation.amount}'.` ); } if (data.donation.frequencyUnit) { data.donation.frequencyUnit = PROCA_FREQUENCIES[data.donation.frequencyUnit] || data.donation.frequencyUnit; } console.debug("Donation Data for Proca", data.donation); return await addActionContact("donate", actionPage, data, test); } async function addActionContact(actionType, actionPage, data, test) { var query = `mutation addActionContact( $action: ActionInput!, $contact:ContactInput!, $privacy:ConsentInput!, $contactRef:ID, $actionPage:Int!, $tracking:TrackingInput ){ addActionContact( actionPageId: $actionPage, action: $action, contactRef:$contactRef, contact:$contact, privacy:$privacy, tracking:$tracking ){contactRef,firstName} } `; let privacy = { optIn: data.privacy === "opt-in" || data.privacy === "opt-in-both", leadOptIn: data.privacy === "opt-in-both" || data.privacy === "opt-in-lead", }; if (!data.privacy) // case where the consent wasn't given because not asked privacy = {}; const expected = //"uuid,firstname,lastname,email,phone,country,postcode,street,locality,address,region,birthdate,privacy,tracking,donation".split( "uuid,firstname,lastname,email,phone,country,postcode,address,region,birthdate,privacy,tracking,donation".split( "," ); const variables = { actionPage: actionPage, action: { actionType: actionType, customFields: {}, // added below }, contact: { firstName: data.firstname, lastName: data.lastname, email: data.email, phone: data.phone, address: { country: data.country || "", postcode: data.postcode || "", // street: data.street || "", bug: street not saved properly in the address field, creates an internal error on mtt }, }, privacy: privacy, }; if (test) variables.action.testing = true; if (data.targets) { if (data.mttProcessing !== false) { variables.action.mtt = { subject: data.subject, body: data.message, targets: data.targets.map(d => d.procaid), }; delete data.message; delete data.subject; } delete data.targets; } if (data.donation) variables.action.donation = data.donation; if (data.uuid) variables.contactRef = data.uuid; if (data.region) variables.contact.address.region = data.region; //if (data.locality) variables.contact.address.locality = data.locality; if (data.birthdate) variables.contact.birthDate = data.birthdate; if (data.tracking && Object.keys(data.tracking).length) { variables.tracking = data.tracking; } for (const [key, value] of Object.entries(data)) { if (value && !expected.includes(key)) variables.action.customFields[key] = value; } variables.action.customFields = JSON.stringify(variables.action.customFields); const response = await graphQL("addActionContact", query, { variables: variables, }); if (response.errors) return response; return response.addActionContact; } async function stripeCreateCustomer(actionPageId, contactDetails) { var query = `mutation addStripeObject ( $actionPageId: Int!, $customer: Json, ) { addStripeObject ( actionPageId: $actionPageId, customer: $customer, ) } `; const response = await graphQL("addStripeObject", query, { variables: { actionPageId: actionPageId, customer: JSON.stringify(contactDetails), }, }); if (response.errors) return response; const customer = JSON.parse(response.addStripeObject); // console.log("customer create stripe response", customer); return customer; } async function stripeCreate(params /* pageId, amount, currency, contact,*/) { const customer = await stripeCreateCustomer( params.actionPage, params.contact ); const amount = params.amount; const currency = params.currency; const actionPage = params.actionPage; const isSubscription = params.frequency && params.frequency !== "oneoff"; if (isSubscription) { const frequency = params.frequency; return await stripeCreateSubscription( { actionPage, customer, frequency, amount, currency, }, params ); } return await stripeCreatePaymentIntent( { actionPage, customer, amount, currency }, params ); } async function stripeCreatePaymentIntent({ actionPage, customer, amount, currency, }) { var query = `mutation addStripeObject ( $actionPageId: Int!, $customer: Json, $price: Json, $subscription: Json, $paymentIntent: Json ) { addStripeObject ( actionPageId: $actionPageId, customer: $customer, price: $price, subscription: $subscription, paymentIntent: $paymentIntent ) } `; const variables = { actionPageId: actionPage, paymentIntent: JSON.stringify({ amount: amount, currency: currency, setup_future_usage: "off_session", customer: customer.id, }), }; // console.debug("GraphQL query ", query, variables); const response = await graphQL("addStripeObject", query, { variables: variables, }); // console.debug("Proca Response ", response); if (response.errors) return response; const stripeResponse = JSON.parse(response.addStripeObject); // console.debug("Stripe response ", stripeResponse); return { response: stripeResponse, client_secret: stripeResponse.client_secret, }; } async function stripeCreateSubscription( { actionPage, customer, amount, currency, frequency }, params ) { var query = `mutation addStripeObject ( $actionPageId: Int!, $customer: Json, $price: Json, $subscription: Json, $paymentIntent: Json ) { addStripeObject ( actionPageId: $actionPageId, customer: $customer, price: $price, subscription: $subscription, paymentIntent: $paymentIntent ) } `; // const STRIPE_RECURRING_INTERVALS = { // weekly: 'week', // monthly: 'month', // daily: 'day', // yearly: 'year', // }; // items[0][price_data][recurring][interval].", const subscription = { payment_behavior: "default_incomplete", metadata: params.metadata || {}, customer: customer.id, items: [ { price_data: { unit_amount: amount, currency: currency, product: params.stripe_product_id, recurring: { interval: frequency, interval_count: 1 }, }, }, ], expand: ["latest_invoice.payment_intent"], }; const response = await graphQL("addStripeObject", query, { variables: { actionPageId: actionPage, subscription: JSON.stringify(subscription), }, }); if (response.errors) return response; const stripeResponse = JSON.parse(response.addStripeObject); // console.debug(" Create Subscription Response:", stripeResponse); return { subscriptionId: stripeResponse.id, client_secret: stripeResponse.latest_invoice.payment_intent.client_secret, response: stripeResponse, }; } const errorMessages = errors => { return errors.map(({ message }) => message).join(", "); }; export { addActionContact, addDonateContact, addAction, getCount, getCountByName, getLatest, graphQL, stripeCreatePaymentIntent, stripeCreate, errorMessages, };