Integration with Salesforce using Python is a powerful capability,
particularly when leveraging the simplicity of the Simple Salesforce API.
In this use case, the objective is to create leads with attachments using Python and Flask, making use of Salesforce’s REST API functionality.
For the purpose of writing a blog, this line succinctly captures the essence of the task at hand: ‘We can leverage the Simple Salesforce API to seamlessly integrate and create leads with attachments, utilizing the robust functionality of Salesforce’s REST API in Python with Flask.
Step (1): Create a connected app in salesforce
Create a connected app for authentication and authorization, enabling the use of a password with a security token. To obtain the client ID and client secret, simply create a connected app in Salesforce. It’s a straightforward process—just create and allow REST API access; that’s it.
Step (2): Now, install Python on your local machine and use the PyCharm Community Edition editor for running Python locally.
(i) Import Salesforce SimpleAPI.
(ii) Also, import Flask for the frontend web application.
(iii) Import all the libraries by simply clicking the code and importing; this allows direct importing from the editor.
(iv) Change the username, password, client ID, and client secret on your own. For best practices, you can separate these configurations somewhere else. This is a demo org, which is why I am providing this for understanding purposes.
(v) Create a “templates” folder; the name should be exactly “templates” only. Then, add an HTML file inside and link it with the main Python file, which I am currently using for LeadCreateWithAttachments.py
LeadCreateWithAttachments.py
import json
import os
from flask import Flask, render_template, request, redirect, url_for, jsonify
import base64
import requests
#make sure to import all the packages first
params = {
"grant_type": "password",
"client_id": "Your clientId",
"client_secret": "Your clientId",
"username": "yourUserName",
"password": "password + securityToken" #without any space
}
r = requests.post("https://login.salesforce.com/services/oauth2/token", params=params)
access_token = r.json().get("access_token")
instance_url = r.json().get("instance_url")
print("Access Token:", access_token)
print("Instance URL", instance_url)
def sf_api_call(action, parameters={}, method='get', data={}):
headers = {
'Content-type': 'application/json',
'Accept-Encoding': 'gzip',
'Authorization': 'Bearer %s' % access_token
}
if method == 'get':
r = requests.request(method, instance_url + action, headers=headers, params=parameters, timeout=30)
elif method in ['post', 'patch']:
r = requests.request(method, instance_url + action, headers=headers, json=data, params=parameters, timeout=10)
else:
raise ValueError('Method should be get or post or patch.')
print('Debug: API %s call: %s' % (method, r.url))
if r.status_code < 300:
if method == 'patch':
return None
else:
return r.json()
else:
raise Exception('API error when calling %s : %s' % (r.url, r.content))
app = Flask(__name__)
@app.route('/')
def index():
return render_template('LeadCreateWithAttachment.html')
@app.route('/submit', methods=['POST'])
def submit():
# Get form data from the request
first_name = request.form['firstName']
last_name = request.form['lastName']
company = request.form['company']
# Salesforce REST API endpoint for creating a Lead
api_endpoint = f"{instance_url}/services/data/v60.0/sobjects/Lead/"
# Prepare data for the Lead object
lead_data = {
'FirstName': first_name,
'LastName': last_name,
'Company': company
}
# Prepare headers with authorization token
headers = {
'Authorization': 'Bearer ' + access_token,
'Content-Type': 'application/json'
}
response = requests.post(api_endpoint, headers=headers, json=lead_data)
print("Lead Creation Response:", response.json()) # Add this line for debugging
if response.status_code == 201:
# return upload_file(lead_id)
lead_id = response.json().get('id')
upload_file(lead_id);
return 'Lead created successfully with attachments'
else:
# Error creating Lead
return jsonify({'error': 'Failed to create Lead'})
@app.route('/upload', methods=['POST'])
def upload_file(lead_id):
file = request.files['file']
if file:
encoded_string = base64.b64encode(file.read()).decode("utf-8")
content_version = sf_api_call('/services/data/v60.0/sobjects/ContentVersion', method="post", data={
'Title': file.filename,
'PathOnClient': file.filename,
'VersionData': encoded_string,
})
content_version_id = content_version.get('id')
content_version = sf_api_call(f'/services/data/v60.0/sobjects/ContentVersion/{content_version_id}')
content_document_id = content_version.get('ContentDocumentId')
# Create a ContentDocumentLink
content_document_link = sf_api_call('/services/data/v60.0/sobjects/ContentDocumentLink', method='post', data={
'ContentDocumentId': content_document_id,
'LinkedEntityId': lead_id,
'ShareType': 'V'
})
return f'Successfully uploaded {file.filename} and linked to record with Id {lead_id}!'
else:
return 'No file uploaded.'
if __name__ == '__main__':
app.run(debug=True)
Here, we are creating an LWC (Lightning Web Components) to check whether the “Enable Opportunity Team” , “Enable OpportunitySplit” and “Multi-Currency” features are enabled. We have designed an LWC component along with a button. When the button is clicked, it invokes an Apex method to verify the enablement status of the specified objects. If any of the features are enabled, the checkbox will be checked; otherwise, it will remain unchecked.
To make a Lightning Web Component (LWC) visible to users, we set up custom settings. In these settings, there are four fields: Last_Run_Date__c, Opportunity_Team__c, Opportunity_Split__c, and Multi_Currency_enabled__c. These fields hold the latest values, making sure that the component shows the most up-to-date information
Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you.
Setting up Stripe in your app is relatively straightforward. Begin by creating your account on Stripe and obtaining the secret access keys. Once you have these keys, you are ready to utilize their API for processing payments. For testing purposes, Stripe offers test credentials to validate your payment flow.
Stripe provides well-organized API documentation that makes it easy to get started. In this article, we will use Salesforce Apex language to sync accounts from Salesforce to Stripe as customers. This synchronization will include opportunity price and amount data from Salesforce, ensuring that it corresponds with the customer invoice in Stripe.
1. Create Stripe Account
In order to use Stripe, you need to register yourself on Stripe.
2.Get Secret key and publishable key
Go to the developers’ section in Stripe and click here to find the API key section. Copy both the publishable key and the secret key. Once you have located these keys, copy and save them in Salesforce custom settings.
In the custom setting, create two fields: “Secret Test Key” and “Publishable Test Key.” Add the required data and save it. Furthermore, when integrating with Apex, we can utilize these keys through a method.
3.Establish a connection between Stripe and Salesforce via Apex.
You need to create a class and a method for this connection. In order to establish the connection, create two separate classes for this purpose: one is “StripeCallout,” and the second is “StripeApi” for handling the API key. You can also refer to the API documentation provided by Stripe for additional guidance
global class StripeAPI {
global static String ApiKey{
get{
StripeSettings__c s = StripeSettings__c.getInstance();
if(s!=null){
System.debug('s.Stripe_Secret_Test_Key__c is ==>'+s.Stripe_Secret_Test_Key__c);
return s.Stripe_Secret_Test_Key__c;
}
return null;
}
}
}
4. Test your connections
Now, you can test your connection to check whether you receive a “200 OK” status. If you receive an “OK,” it indicates that your connection is ready to sync the data. Additionally, we are using the account record to sync as a customer from Salesforce to Stripe.
I have created a separate Visualforce page for this purpose and a controller to pass the data at runtime via an action button. The customer ID is also stored as a response. In order to sync the invoice, you have to pass the customer ID and then synchronize the invoice for that particular customer.
public class StripeCustomerController {
public StripeCustomerController(){
}
public static Id accId{get;set;}
public Account acc {get;set;}
public StripeCustomerController(ApexPages.StandardController controller){
accId = controller.getRecord().Id;
System.debug('acc is '+acc);
}
@future(callout = true)
public static void createCustomer(Id accId){
Account acc = StripeCustomerHelper.getAccount(accId);
HttpResponse responses = StripeCallout.makeCallout(acc);
system.debug('responses'+responses);
CustomerResponse cr = CustomerResponse.parse(responses.getbody());
System.debug('crid---------s'+cr);
Update new Account (id=accId,CustomerID__c=cr.id);
}
public Pagereference syncStripe(){
StripeCustomerController.createCustomer(accId);
return new PageReference('/'+accId);
}
}
public class StripeCustomerHelper {
public static Account getAccount(Id accId){
return [SELECT Id,
Name,
ShippingCity,
ShippingStreet,
ShippingState,
ShippingCountry,
ShippingPostalCode,
phone,
Email__c,
BillingCity,
BillingStreet,
BillingCountry,
BillingPostalCode,
BillingState,
BillingLatitude,
BillingLongitude
FROM Account
WHERE Id =: accId];
}
}
After completing these steps, create a button on the Account record page and call the Visualforce page, then save it. Add the button to the page layout and save the changes.
After clicking the button, your account data, including name, email, and phone, will sync with the Stripe customer. You can add more fields as needed based on your business requirements. Now, check your Stripe dashboard and click on “Customers”; you will see the synced customer.
6. Opportunity sync as invoice
Afterward, create an opportunity related to the account, add the amount, price, description, currency, and save it.
Now, I have created a separate class and method to send the opportunity as an invoice from Salesforce. Create a wrapper class for this to receive the response
public class Stripe{
String API_KEY='';
public Stripe(string apiKey){
API_KEY = apiKey;
}
public StripeResponseModel createInvoice(String customer,String amount,String curency,String description){
StripeInvoice strInv=new StripeInvoice(API_KEY);
return strInv.createInvoice(customer,amount,curency,description);
}
}
public class StripeOpportunityHelper {
public static Opportunity getOpportunity(Id oppId){
return [SELECT id,AccountId,Amount,Account.CustomerID__c,Account.Name,Description FROM Opportunity WHERE Id =: oppId];
}
}
public class StripeOpportunityController {
public StripeOpportunityController(){
}
public static Id oppId{get;set;}
public Opportunity opp {get;set;}
public StripeOpportunityController(ApexPages.StandardController controller){
oppId = controller.getRecord().Id;
opp = StripeOpportunityHelper.getOpportunity(oppId);
System.debug('opp is '+opp);
Stripe request = new Stripe(StripeAPI.ApiKey);
StripeResponseModel response = request.createInvoices(opp.Account.CustomerID__c,String.valueOf(Integer.valueOf(opp.Amount)), 'inr', opp.Description);
}
public Pagereference syncStripe(){
//acc = StripeCustomerHelper.createOrUpdateCustomer(acc);
//update acc;
return new PageReference('/'+oppId);
}
}
public class StripeResponseModel{
public string id {get;set;}
public string rObject{get;set;}
public string amount {get;set;}
public string paid {get;set;}
public String curency{get;set;}
public string fee {get;set;}
public string created {get;set;}
public string livemode {get;set;}
public string description {get;set;}
public string refunded {get;set;}
public boolean isError {get;set;}
public String name{get;set;}
public String interval{get;set;}
public StripeCardResponseModel card {get;set;}
public StripeErrorModel errorResponse {get;set;}
public StripeResponseModel(){
card=new StripeCardResponseModel();
errorResponse=new StripeErrorModel();
}
}
Map all these classes and methods, and also add the button for the Opportunity custom button to sync the opportunity. You can also incorporate additional logic or conditions based on your business requirements.
After completing these steps, click the “Sync Invoice” button. You will find the invoice items inside the Stripe customer. You can convert the invoice and send a payment link to that particular customer via their email.
“Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you. Your feedback is invaluable as we strive to provide content that matters to you. Stay tuned for more enriching articles, and once again, thank you for being part of our reading community!”
XERO is a cloud-based accounting software platform that allows businesses to manage their finances in real-time.
“Here, we have integrated Xero with Salesforce.
First, you need to create a Xero account. Inside the account, create an app and generate the client ID and client secret for authorization and authentication. You can refer to this website for your API reference and documents as well.”
1. Create Xero Account
In order to use Xero, you need to register yourself on Xero.
2.Get Client id and Client secret
Go to this link, create an app, and obtain the client ID and client secret for the Configurations tab.
3.Create Auth providers in salesforce .
Create Auth providers in Salesforce and fill in all the information in the Auth Provider. After that, provide the callback URL from Salesforce to register the callback URL. Prompt the authentication, allow, and accept the terms and conditions. for more details click the link.1 ,link2
4.Account Sync Process.
we are using the account record to sync as a contact from Salesforce to Xero.
I have created a separate Visualforce page for this purpose and a controller to pass the data at runtime via an action button. The customer ID is also stored as a response. In order to sync the invoice , you have to pass the customer ID and then synchronize the invoice for that particular Contact.
public class XeroCustomerController {
public XeroCustomerController(){
}
public static Id accId{get;set;}
public Account acc {get;set;}
public XeroCustomerController(ApexPages.StandardController controller){
accId = controller.getRecord().Id;
Account acc = XeroCustomerHelper.getAccount(accId);
System.debug('acc is '+acc);
}
@future(callout = true)
public static void createCustomer(Id accId){
Account acc = XeroCustomerHelper.getAccount(accId);
XeroContact xeroContact = new XeroContact(acc);
try {
XeroContact responseContact = XeroAPI.sendContact(xeroContact);
System.debug('Contact sent successfully to Xero. Xero ID: ' + responseContact.ContactID);
update new Account (id=accId,Xero_Customer_ID__c=responseContact.ContactID);
} catch (XeroApiException e) {
System.debug('Error sending contact to Xero: ' + e.getMessage());
}
}
public Pagereference syncXero(){
XeroCustomerController.createCustomer(accId);
return new PageReference('/'+accId);
}
}
public class XeroCustomerHelper {
public static Account getAccount(Id accId){
return [SELECT Id,
Name,
ShippingCity,
ShippingStreet,
ShippingState,
ShippingCountry,
ShippingPostalCode,
phone,
fax,
Email__c,
BillingCity,
BillingStreet,
BillingCountry,
BillingPostalCode,
BillingState,
BillingLatitude,
BillingLongitude
FROM Account
WHERE Id =: accId];
}
}
global class XeroTenant {
global static String getXeroTenantId () {
HttpResponse response = XeroServiceClass.getCallout('GET', 'connections');
if (response.getStatusCode() < 300) {
List<XeroConnection> xeroConnections = (List<XeroConnection>) JSON.deserialize(response.getBody(), List<XeroConnection>.class);
return xeroConnections[0].tenantId;
}
else {
System.debug('error occurred');
}
}
}
public with sharing class XeroConnection {
public String id;
public String tenantId;
public String tenantType;
public String createdDateUtc;
public String updatedDateUtc;
}
5. Account setup in Salesforce
After completing these steps, create a button on the Account record page and call the Visualforce page, then save it. Add the button to the page layout and save the changes.
After clicking the button, your account data, including name, Phone, billing address,shipping address will sync with the Xero contact. You can add more fields as needed based on your business requirements. Now, check your xero dashboard and click on “contacts”; you will see the synced contact.
6. Opportunity (Product) sync as invoice
Afterward, create an opportunity related to the account, add the amount, price, description, currency, product list of product and save it.
Now, I have created a separate class and method to send the opportunity product as an invoice from Salesforce. Create a wrapper class for this to send JSON serialized data.
public class XeroInvoice {
public XeroInvoice(){
}
public String Type; //ACCREC
public String InvoiceID; //94b8a8d6-31fd-4d8c-8791-d1a2a7dfe898
public String InvoiceNumber; //asdsdasd
public String Reference; //ABC123
public XeroContact Contact;
public String Date_x;
public String DueDate;
public Boolean SentToContact;
public Decimal AmountDue; //115
public Decimal AmountPaid; //0
public Decimal AmountCredited; //0
public Decimal CurrencyRate; //1
public Boolean IsDiscounted;
public Boolean HasAttachments;
public Boolean HasErrors;
public String Status; //AUTHORISED
public String LineAmountTypes;
public Decimal SubTotal; //100
public Decimal TotalTax; //15
public Decimal Total; //115
public String UpdatedDateUTC;
public String CurrencyCode;
public String Url;
public List<LineItem> LineItems;
public List<Payment> Payments;
public List<CreditNote> CreditNotes;
public List<PrePayment> Prepayments;
public List<OverPayment> Overpayments;
public List<XeroValidationError> ValidationErrors;
public class Payment {}
public class CreditNote {}
public class PrePayment {}
public class OverPayment {}
public class LineItem {
public String LineItemID; //006fc261-ee0b-4f4c-bb55-c96ca97cdb53
public String Description; //
public String AccountCode; //200
public String TaxType; //OUTPUT2
public Decimal UnitAmount; //100
public Decimal TaxAmount; //15
public Decimal LineAmount; //100
public Decimal Quantity; //1
}
public XeroInvoice(String xeroContactId) {
this.Type = 'ACCREC';
this.Contact = new XeroContact(xeroContactId);
}
public String serialize() {
String serialized = JSON.serialize(this, true);
serialized = serialized.replace('"Date_x"', '"Date"');
return serialized;
}
}
public class XeroOpportunityController {
public XeroOpportunityController(){
}
public static Id oppId{get;set;}
public Opportunity opp {get;set;}
public List<OpportunityLineItem> opplineItem {get;set;}
public XeroOpportunityController(ApexPages.StandardController controller){
oppId = controller.getRecord().Id;
List <OpportunityLineItem> lstOpportunityLineItem = XeroOpportunityHelper.getOpportunityProductLineItem(oppId);
System.debug('lstOpportunityLineItem is =======================================>'+lstOpportunityLineItem);
Set<String> customerIdSet = new Set<String>();
List<XeroInvoice.LineItem> lstInvoiceItem = new List<XeroInvoice.LineItem>();
for(OpportunityLineItem opplist :lstOpportunityLineItem){
customerIdSet.add(opplist.Opportunity.Account.Xero_Customer_ID__c);
}
System.debug('customerIdSet is =======================================>'+customerIdSet);
for(OpportunityLineItem opplist :lstOpportunityLineItem){
XeroInvoice.LineItem lineItem = new XeroInvoice.LineItem();
lineItem.Description = opplist.Product2.Name;
lineItem.Quantity = opplist.Quantity;
lineItem.UnitAmount = opplist.UnitPrice;
// lineItem.TaxAmount = 10.00;
lineItem.LineAmount = opplist.TotalPrice;
lstInvoiceItem.add(lineItem);
}
//-------------------------------
System.debug('lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c-------======'+lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c);
XeroInvoice newInvoice = new XeroInvoice(lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c);
newInvoice.Date_x = String.valueof(Date.today());
newInvoice.DueDate = String.valueof(Date.today()+15);
newInvoice.Reference = 'Website Design';
newInvoice.LineItems = lstInvoiceItem;
//newInvoice.SubTotal = 100.00;
//newInvoice.TotalTax = 15.00;
//newInvoice.Total = 115.00;
try {
XeroInvoice resultInvoice = XeroAPICall.sendInvoice(newInvoice);
System.debug('Invoice successfully sent to Xero. InvoiceID: ' + resultInvoice.InvoiceID);
} catch (Exception e) {
System.debug('Error sending invoice to Xero: ' + e.getMessage());
}
//-------------------------------
}
public Pagereference syncXero(){
//return to the same page
return new PageReference('/'+oppId);
}
}
public class XeroOpportunityHelper {
public static List<OpportunityLineItem> getOpportunityProductLineItem(Id oppId){
return [select id,
TotalPrice,
UnitPrice,
Name,
OpportunityId,
Opportunity.Account.Name,
Opportunity.Amount,
Opportunity.Account.Xero_Customer_ID__c ,
Quantity,
ListPrice,
Product2.Name,
product2.Id from
OpportunityLineItem
where OpportunityId=: oppId];
}
}
Map all these classes and methods, and also add the button for the Opportunity custom button to sync the opportunity. You can also incorporate additional logic or conditions based on your business requirements.
After completing these steps, click the “Sync Invoice” button. You will find the invoice items inside the Xero contact.
“Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you. Your feedback is invaluable as we strive to provide content that matters to you. Stay tuned for more enriching articles, and once again, thank you for being part of our reading community!”
And then map all the invoices in which you just sent the products as an invoice, or you can use LWC to sync the product.”
<apex:page standardcontroller="Invoice__c" extensions="SendInvoiceController" action="{!syncQB}">
</apex:page>
public class SendInvoiceController{
public Id invId{get;set;}
public Invoice__c inv {get;set;}
public SendInvoiceController(ApexPages.StandardController controller) {
invId = controller.getRecord().Id;
inv = [SELECT Id,
Name,
QBO_Id__c,
QBO_SyncToken__c,
Description__c,
Invoice__c.Opportunity__r.Name,
Invoice__c.Opportunity__r.Account.contact__r.email,
Invoice__c.Opportunity__r.Account.QBO_Id__c,
Invoice__c.opportunity__r.account.BillingCity,
Invoice__c.opportunity__r.account.name,
FROM Invoice__c
WHERE Id =: invId];
}
public Pagereference syncQB(){
AuthCallout callout = new AuthCallout ();
QBInvoiceQueryJSON queryJson = callout.getInvoice(inv);
if(queryJson != null){
if(queryJson.QueryResponse.Invoice.size() == 1){
inv.QBO_Id__c = queryJson.QueryResponse.Invoice[0].Id;
inv.QBO_SyncToken__c = queryJson.QueryResponse.Invoice[0].SyncToken;
}else{
throw new Exception();
}
}
InvoiceResponseJSON res = AuthCallout.createInvoiceCall(inv);
callout.updateRefreshToken();
inv.QBO_Id__c = res.Invoice.Id;
inv.QBO_SyncToken__c = res.Invoice.SyncToken;
inv.tax__c = res.Invoice.TxnTaxDetail.TotalTax;
inv.total__c = res.Invoice.TotalAmt;
update inv;
return new PageReference('/'+ invId);
}
}
Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you.