This is the Fedex new API Calling method of getting rate and other infomation

The Job:


For many years, I have been downloading FedEx's rate charts into FoxPro tables and using this to compute the freight cost for an invoice in our accounting system.  The accounting system is written in FoxPro 6.0 and includes source code so this makes modifications very easy.  However, there is very little information on how to access FedEx's API from FoxPro.
The project is to send to FedEx everything necessary to compute the rate.  Source location, destination, box count, box weights, box sizes, declared value, COD and any other relevant information and get back either the list rate or my contractual rate, all from inside my FoxPro program in real time.

This blog only covers one of the many API functions FedEx is making available.  Getting the price is the one I wanted,  You might want to get quotes on all available services for the information you pass to it.  You can setup a shipment and print the labels,  Get address validations.  There is a list on the developer site.  https://developer.fedex.com/api/en-us/home.html

Lets go through this.  Create an account on the developer site, then you need to create an organization and to this organization, you would need to add a project like getrate.  When you do this, you will need to add an API to the project.  Choose Ship Rates and at the bottom, choose rates and transit times API. You would also need to associate your FedEx account to this organization so that their rates get returned. 

Once this is setup correctly, we can work on the code
Read every line carefully.  There are places that you will need to replace your information and I have chosen options that you might not want. and you might have information such as dangerous goods that I don't have to deal with.

And of course the other part is I have no idea where you are generating your information.  Do you have box sizes and weights? I load an array in the calling program.  I have my address but you need to feed it your address and the destination address.  So you will need to be intimately familiar with this code.  But this should get you most of the way.

***this version is to construct the json call using the new api
SET TALK OFF
SET SAFETY OFF
**this is for testing
***this version is to construct the json call using the new api
SET TALK OFF
SET SAFETY OFF
IF SYS(0)="YOUR COMPUTER NAME"
  mstandalone=.F.
ELSE
  mstandalone=.F.
ENDIF
malias=""
codpkg=.T.
STORE 0 TO minsur,codamt,mdas,fuel,mresdel
mresidential="false"
WAIT WINDOW "Contacting FedEx's servers for quote." NOWAIT
IF mstandalone
  mservice="FEDEX_GROUND"
  mcity="brooklyn"
  mstate="NY"
  mzip="11217"
  mcountrycode="US"
  mresidential="false"
  rcod=.F.
  mshipmentvalue=318
ELSE
  malias=ALIAS()
  rcod=mcod
  IF mshipresid
    mresidential="true"
  ENDIF
  DO CASE
  CASE mservice="FEDEXGRND" AND mshipresid
    mservice="GROUND_HOME_DELIVERY"
  CASE mservice="FEDEXGRND"
    mservice="FEDEX_GROUND"
  CASE mservice="FEDEX HOME"
    mservice="GROUND_HOME_DELIVERY"
    mshipresid=.T.
  CASE mservice="FEDEX PO "
    codpkg=.F.
    mservice="PRIORITY_OVERNIGHT"
  CASE mservice="FEDEX SO "
    codpkg=.F.
    mservice="STANDARD_OVERNIGHT"
  CASE mservice="FEDEX 2DAY "
    codpkg=.F.
    mservice="FEDEX_2_DAY"
  CASE mservice="2DAYAM "
    codpkg=.F.
    mservice="FEDEX_2_DAY_AM"
  CASE mservice="EXPRESS "
    codpkg=.F.
    mservice="FEDEX_EXPRESS_SAVER"
  CASE mservice="FEDEX FO "
    codpkg=.F.
    mservice="FIRST_OVERNIGHT"
  CASE mservice="FEDEXINTECO "
    mservice="INTERNATIONAL_ECONOMY"
  CASE mservice="FEDEXINTPR "
    mservice="INTERNATIONAL_PRIORITY"
  CASE mservice="FEDEXINTFO "
    mservice="INTERNATIONAL_FIRST"
  OTHERWISE
    mservice="FEDEX_GROUND"
  ENDCASE
  mshipmentvalue=mdollar
ENDIF
raddress1=" "
raddress2=" "
rcity=mcity &&"dunstable"
rstate=mstate && "MA"
rzip=mzip &&"01827"
rcountry=mcountrycode
mactweight=0
**convert the MB aray into this array
IF mstandalone
  DIME marray(2,4)
  marray(1,1)=12 &&WEIGHT
  marray(1,2)=12 &&LENGTH
  marray(1,3)=12 &&WIDTH
  marray(1,4)=12 &&DEPTH
  marray(2,1)=12 &&WEIGHT
  marray(2,2)=12 &&LENGTH
  marray(2,3)=12 &&WIDTH
  marray(2,4)=12 &&DEPTH
  mactualweight=1
  mboxvalue=mshipmentvalue/ALEN(marray,1)
ELSE
  SET COMPATIBLE OFF
  FOR a=1 TO ALEN(mb,1)
    IF NOT EMPTY(mb(a,1))
      DIME marray(a,4)
      marray(a,1)=mb(a,3) &&WEIGHT
      marray(a,2)=mb(a,8) &&LENGTH
      marray(a,3)=mb(a,9) &&WIDTH
      marray(a,4)=mb(a,10) &&DEPTH
    ENDIF
    mactweight=mactweight+mb(a,4)
    mboxvalue=mshipmentvalue/ALEN(marray,1)
  NEXT
  SET COMPATIBLE ON
ENDIF
mtotalcnt=0
mtotalweight=0
FOR a=1 TO ALEN(marray,1)
    mtotalcnt=mtotalcnt+1
    mtotalweight=mtotalweight+marray(a,1)
ENDFOR
**you will be given the sandbox info.  Later when you get it working, you can add the production under it which will supersede the sandbox keys
**sandbox keys
mclientid = Enter your sandbox client ID here"
mclientsecret = "Enter your client secret here"
lcurl="https://apis-sandbox.fedex.com/oauth/token"
*production keys
mclientid = "Enter your production client ID here"
mclientsecret = "Enter your production clientsecret here"
lcurl="https://apis.fedex.com/oauth/token"

*if you have read my quickbooks blog, there is a need to choose a client company.  Here there is not so we can skip
*that authorization portion. 
**every time you do this, you will be handed a token which will be included in the API call

* Begin the OAuth2 Authorization code flow. This returns a URL that should be loaded in a browser.
*?"Start the flow."

************************oauth2 flow *******************

lcdata= ;
  "grant_type=client_credentials" + ;
  "&client_id=" + mclientid + ;
  "&client_secret=" + mclientsecret
lohttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1")
lohttp.OPEN("POST", lcurl, .F.)
lohttp.setrequestheader("Content-type","application/x-www-form-urlencoded")
lohttp.setrequestheader("Accept","Application/json")

lohttp.SEND(lcdata)
IF lohttp.STATUS = 200
    lcresponse = lohttp.responsetext
    * ? lcResponse
ELSE
    ? "HTTP Error:"+lohttp.STATUS
    ? lohttp.responsetext
ENDIF

lojson = jsonparse(lohttp.responsetext)

lcaccesstoken = lojson.access_token
lctokentype = lojson.token_type
lnexpiresin = lojson.expires_in
lcscope = lojson.scope
********************end of oauth2 flow *********************
 

*it is that easy


*******************API call*********************************
RELEASE lohttp
**I have superseded this URL with the production endpoint
lcurl="https://apis-sandbox.fedex.com/rate/v1/rates/quotes"
lcurl="https://apis.fedex.com/rate/v1/rates/quotes"
*Sandbox account #
**lcdata=lcdata+' "accountNumber": { "value": "740561073" },'

lcdata='{'
**enter the account number instead of the 123456789
lcdata=lcdata+' "accountNumber": { "value": "123456789" },'
lcdata=lcdata+' "rateRequestControlParameters": {'
lcdata=lcdata+' "returnTransitTimes": false'
lcdata=lcdata+' },'
lcdata=lcdata+' "requestedShipment": {'
lcdata=lcdata+' "rateRequestType": ["LIST"],'
lcdata=lcdata+' "totalPackageCount": '+ALLT(STR(mtotalcnt,5))+','
lcdata=lcdata+' "serviceType": "'+mservice+'",'
lcdata=lcdata+' "shipper": { "address": { "postalCode": "type your zip code here", "countryCode": "US" } },'
lcdata=lcdata+' "recipient": { "address": { "postalCode": "'+rzip+'", "countryCode": "US","residential": '+mresidential+' } },'
lcdata=lcdata+' "pickupType": "USE_SCHEDULED_PICKUP",'
lcdata=lcdata+' "packagingType": "YOUR_PACKAGING",'
IF (rcod AND rcountry="US") AND codpkg
  lcdata=lcdata+' "specialServicesRequested": {'
  lcdata=lcdata+' "codDetail": {'
  lcdata=lcdata+' "codCollectionAmount": { "amount": 500.00, "currency": "USD" },'
  lcdata=lcdata+' "collectionType": "GUARANTEED_FUNDS" '
  lcdata=lcdata+' }'
  lcdata=lcdata+' },'
ENDIF
lcdata=lcdata+' "requestedPackageLineItems": ['
FOR a=1 TO ALEN(marray,1)
  lcdata=lcdata+'{'
  lcdata=lcdata+' "weight": { "units": "LB", "value": '+ALLTRIM(STR(marray(a,1),5,1))+' },'
  lcdata=lcdata+' "dimensions": { "length": '+ALLTRIM(STR(marray(a,2),5,0))+', "width": '+ALLTRIM(STR(marray(a,3),5,0))+', "height": '+ALLTRIM(STR(marray(a,4),5,0))+', "units": "IN" },'
  lcdata=lcdata+' "declaredValue": { "amount": '+ALLT(STR(mboxvalue,8))+', "currency": "USD" }'
  lcdata=lcdata+' },'
NEXT
lcdata=LEFT(lcdata,LEN(lcdata)-1)
lcdata=lcdata+' ]'
lcdata=lcdata+' }'
lcdata=lcdata+'}'
lohttp2 = CREATEOBJECT("WinHttp.WinHttpRequest.5.1")
lohttp2.OPEN("POST", lcurl, .F.)
lohttp2.setrequestheader("Content-Type","application/json")
lohttp2.setrequestheader("Authorization","Bearer "+ lcaccesstoken)
lohttp2.SEND(lcdata)
IF lohttp2.STATUS = 200
  lcresponse = lohttp2.responsetext
  * ? lcResponse
  mresult="SUCCESS"
ELSE
  _CLIPTEXT=lohttp2.responsetext
  mresult="HTTP Error:"+ lohttp2.STATUS
  myrate=0
  listrate=0
  mdimweight=0
  ** ? lohttp2.responsetext
  **for diagnostics
  RETURN mresult+SPACE(12)+STR(myrate,12,2)+STR(listrate,12,2)
ENDIF

**parse the resulting text file
* lcJson = your JSON string
lcjson = lcresponse
loscript = CREATEOBJECT("MSScriptControl.ScriptControl")
loscript.language = "JScript"

* Inject JSON into JS context
loscript.addcode([var data = ] + lcjson + [;])
***I found the best way was to write individual procedures to extract exactly what I needed from the JSON
***if all i wanted was the total charge, I could stop there but I may want to display all of the pieces somewhere so I get all of the pieces
myrate=0
DO gettotalcharge
listrate=0
DO getlistrate
mdimweight=0
DO getbillingweight
minsur=0
***insurance has been removed
DO getinsuredvalue
**at this point,
myrate=myrate-minsur
listrate=listrate-minsur
mfuel=0
DO getfuel
mresdel=0
DO getresident
mdas=0
DO getextend
mcodamt=0
DO getcodamt

IF NOT EMPTY(malias)
  SELECT (malias)
ENDIF
WAIT CLEAR
**you may want to return just the total amount
RETURN mresult+STR(mdimweight,12,2)+STR(myrate,12,2)+STR(listrate,12,2)





FUNCTION gettotalcharge
* lcJson = FedEx JSON response string
* Get total net charge for the first shipment
myrate=0
myrate = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].totalNetCharge")
RETURN

FUNCTION getlistrate
* Count shipment details
lnshipmentcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails.length")

lctotallistcharge = 0
lccurrency = "USD"

FOR lns = 0 TO lnshipmentcount - 1
  lcratetype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[" + STR(lns,2)+"].rateType")
  IF lcratetype == "LIST"
    lctotallistcharge = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[" + STR(lns,2)+"].totalNetCharge")
    EXIT
  ENDIF
ENDFOR
listrate=lctotallistcharge
RETURN

FUNCTION getbillingweight

* Get total billed weight
lcweightvalue = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].shipmentRateDetail.totalBillingWeight.value")
lcweightunits = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].shipmentRateDetail.totalBillingWeight.units")
mdimweight=lcweightvalue
RETURN

FUNCTION getinsuredvalue
* Only one shipment
lnpkgcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages.length")

* Prepare array: package#, insured value amount, currency
DIMENSION ainsured[lnPkgCount,3]

FOR lnp = 0 TO lnpkgcount - 1
  lnsurchargecount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges.length")
  lcinsamount = 0
  lccurrency = "USD"

  FOR lnj = 0 TO lnsurchargecount - 1
    lctype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].type")
    IF lctype == "INSURED_VALUE"
      lcinsamount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].amount")
      lccurrency = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.currency")
      EXIT && Found Insured Value
    ENDIF
  ENDFOR

  ainsured[lnP+1,1] = lnp+1
  ainsured[lnP+1,2] = lcinsamount
  ainsured[lnP+1,3] = lccurrency
ENDFOR

FOR lni = 1 TO lnpkgcount
  minsur= minsur + ainsured[lnI, 2]
ENDFOR
RETURN


FUNCTION getfuel
* Count packages in the first service
* Only one shipment
lnpkgcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages.length")
* Prepare array: package#, fuel surcharge amount, currency
DIMENSION afuel[lnPkgCount,3]

FOR lnp = 0 TO lnpkgcount - 1
  lnsurchargecount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges.length")
  lcfuelamount = 0
  lccurrency = "USD"

  FOR lnj = 0 TO lnsurchargecount - 1
    lctype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].type")
    IF lctype == "FUEL"
      lcfuelamount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].amount")
      lccurrency = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.currency")
      EXIT && Found Fuel Surcharge
    ENDIF
  ENDFOR

  afuel[lnP+1,1] = lnp+1
  afuel[lnP+1,2] = lcfuelamount
  afuel[lnP+1,3] = lccurrency
ENDFOR
mfuel=0
FOR lni = 1 TO lnpkgcount
  mfuel= mfuel + afuel[lnI, 2]
ENDFOR
RETURN

FUNCTION getresident
* Only one shipment
lnpkgcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages.length")

* Prepare array: package#, residential surcharge amount, currency
DIMENSION ares[lnPkgCount,3]

FOR lnp = 0 TO lnpkgcount - 1
  lnsurchargecount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges.length")
  lcresamount = 0
  lccurrency = "USD"

  FOR lnj = 0 TO lnsurchargecount - 1
    lctype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].type")
    IF lctype == "RESIDENTIAL_DELIVERY" OR lctype == "RESIDENTIAL"
      lcresamount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].amount")
      lccurrency = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.currency")
      EXIT && Found residential surcharge
    ENDIF
  ENDFOR

  ares[lnP+1,1] = lnp+1
  ares[lnP+1,2] = lcresamount
  ares[lnP+1,3] = lccurrency
ENDFOR

mresdel=0
FOR lni = 1 TO lnpkgcount
  mresdel= mresdel + ares[lnI, 2]
ENDFOR
RETURN

FUNCTION getextend
* Only one shipment
lnpkgcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages.length")

* Prepare array: package#, extended area surcharge amount, currency
DIMENSION aextarea[lnPkgCount,3]

FOR lnp = 0 TO lnpkgcount - 1
  lnsurchargecount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages["+ STR(lnp,2)+"].packageRateDetail.surcharges.length")
  lcextamount = 0
  lccurrency = "USD"

  FOR lnj = 0 TO lnsurchargecount - 1
    lctype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].type")
    IF lctype == "OUT_OF_AREA" OR lctype == "EXTENDED_AREA"
      lcextamount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].amount")
      lccurrency = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.currency")
      EXIT && Found Extended Area surcharge
    ENDIF
  ENDFOR

  aextarea[lnP+1,1] = lnp+1
  aextarea[lnP+1,2] = lcextamount
  aextarea[lnP+1,3] = lccurrency
ENDFOR
mdas=0
FOR lni = 1 TO lnpkgcount
  mdas= mdas + aextarea[lnI, 2]
ENDFOR
RETURN

FUNCTION getcodamt
* Only one shipment
lnpkgcount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages.length")

* Prepare array: package#, COD amount, currency
DIMENSION acod[lnPkgCount,3]

FOR lnp = 0 TO lnpkgcount - 1
  lnsurchargecount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges.length")
  lccodamount = 0
  lccurrency = "USD"

  FOR lnj = 0 TO lnsurchargecount - 1
    lctype = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].type")
    IF lctype == "COD"
      lccodamount = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2)+"].packageRateDetail.surcharges[" + STR(lnj,2)+"].amount")
      lccurrency = loscript.EVAL("data.output.rateReplyDetails[0].ratedShipmentDetails[0].ratedPackages[" + STR(lnp,2) + "].packageRateDetail.currency")
      EXIT && Found COD surcharge
    ENDIF
  ENDFOR

  acod[lnP+1,1] = lnp+1
  acod[lnP+1,2] = lccodamount
  acod[lnP+1,3] = lccurrency
ENDFOR

mcodamt=0
FOR lni = 1 TO lnpkgcount
  mcodamt= mcodamt + acod[lnI, 2]
ENDFOR
RETURN


FUNCTION jsonparse(tcjson)
LOCAL losc
losc = CREATEOBJECT("ScriptControl")
losc.language = "JScript"

* Wrap JSON so JScript evaluates it as an object
losc.addcode("function parse(){ return " + tcjson + "; }")
RETURN losc.RUN("parse")
ENDFUNC
Hit Counter