【问题标题】:PayPal Sandbox to Live Transaction Search API not working?PayPal 沙盒到实时交易搜索 API 不起作用?
【发布时间】:2021-02-08 14:01:38
【问题描述】:

注意:我认为我遇到的问题与一般的 PayPal API 问题有关,但无论如何我都会包含我的 R 代码:

我使用 PayPal Transaction Search API 在 R 中编写了一些代码。它只是抓取事务并将它们显示在数据框(表)中。

这与我的 Sandbox 凭据一起工作正常(它显示了我在那里的一项交易)。但是,当我更改为实时凭据时,即使关联的真实账户中有更多交易,也没有数据返回。

为了“上线”,我的代码将请求 URL 更改为“https://api.paypal.com”,并将用户名和密码更改为实时凭据。它没有做任何其他事情。

我想知道是否需要采取其他措施?我已经确保在 Live App 设置下勾选了相关框(因此为 Live App 勾选了 Transaction API)。我还假设开发人员帐户/实时应用程序与其列出的 PayPal 帐户电子邮件相关联,因此将从该(真实)帐户中提取交易。

library(curl)
library(lubridate)
library(rjson)
library(magrittr)
library(dplyr)

# https://cran.r-project.org/web/packages/curl/vignettes/intro.html
# https://developer.paypal.com/docs/integration/direct/transaction-search/
# https://developer.paypal.com/docs/integration/direct/payments/paypal-payments/#


sandbox.creds <- list(username = "sandbox_username",
                password = "sandbox_password")

live.creds <- list(username = "live_username",
                      password = "live_password")

# instantiate vars
api_url <- " "
user_pwd <- " "

access.token <<- NA
access.token.expires <<- NA


live.or.sandbox <- function(live) {
  
  # set these vars to NA whenever this function is called
  # otherwise the live vs. sandbox API will not find the access token
  
  access.token <<- NA
  access.token.expires <<- NA
  
  if (live == TRUE) {
    print("setting to live credentials")
    api_url <<- "https://api.paypal.com"
    user_pwd <<- paste0(live.creds[[1]],":",live.creds[[2]])
  }
  else {
    print("setting to sandbox credentials")
    api_url <<- "https://api.sandbox.paypal.com"
    user_pwd <<- paste0(sandbox.creds[[1]],":",sandbox.creds[[2]])
  }
  
}

# set live or sandbox

live.or.sandbox(live = TRUE)


# body --------------------------------------------------------------------



get.access.token <- function() {
  
      req_access_token <- curl::new_handle() %>%
        handle_setopt(copypostfields = "grant_type=client_credentials", userpwd = user_pwd, verbose=TRUE, httpauth = 1L) %>%
        handle_setheaders("Accept" = "application/json", 
                          "Accept-Language" = "en_US") %>%
        curl_fetch_memory(url = paste0(api_url, "/v1/oauth2/token")) %$% content %>% 
        rawToChar %>% rjson::fromJSON()
      
      access.token.expires <<- lubridate::as_datetime(lubridate::as_datetime(Sys.time()) + lubridate::dseconds(req_access_token$expires_in))
      access.token <<- req_access_token$access_token
  
}

get.access.token.if.current.not.expired <- function() {
  
  current_time <- lubridate::as_datetime(Sys.time()) 
  
  cat("access token state: ", access.token)
  # only request a new access token if one hasn't been generated yet and if previous requests haven't expired
  if (is.na(access.token)) {
    print("no access token")
    get.access.token()
  }
  
  else {
    print("already access token")
    
    if(access.token.expires < as_date(Sys.time())) {
      print("last access token has expired")
      print("get new access token")
      get.access.token()
    }
    
    else {
      print("use old access token")
      cat("time left is ", 
          as.integer(difftime(access.token.expires,
                   as_datetime(Sys.time()),
                   units = "secs")))
    }
    
  }
    
}



make.transaction.request <- function(start_date, end_date) {
  
  # check if new access token required
  get.access.token.if.current.not.expired()
  
  bearer.access.token <- paste0("Bearer ", access.token)
  req_transactions <- new_handle() %>%
    handle_setheaders("Accept" = "application/json", 
                      "Authorization" = bearer.access.token) %>%
    curl_fetch_memory(url = paste0(api_url,"/v1/reporting/transactions?fields=all&start_date=",start_date,"&end_date=", end_date, "&fields=all")) %$% content %>% 
    rawToChar %>% rjson::fromJSON()
  
  print("transaction request")
  print(req_transactions)
  
  # just return transaction details
  req_transactions$transaction_details
}

format.date.rfc339 <- function(date) {

  format(lubridate::as_datetime(date), "%Y-%m-%dT%H:%M:%SZ")
  
} 


# current date in required rfc 339 format
# the maximum date range is 30, so several requests must be made to go beyond this

return.transactions <- function(number_months, tidy_transactions, live) {
  
  if(live == TRUE) {
    live.or.sandbox(live = TRUE)
  }
  
  else {
    live.or.sandbox(live = FALSE)
  }
  
# a month here really means 30 days. 
# thus this function returns number_months * 30 days backwards from the present
  
  current_date <- lubridate::as_datetime(Sys.time())

  # convert dates to rfc339 format
  dates <- lapply(1:number_months, function(x) format.date.rfc339(current_date - ddays((x*30))) )
  
  # get a transaction request for each date range
  res <- list()
  
  for (i in 1:number_months) {
    
    if (i < number_months) {
      start_date <- dates[[i+1]]
      end_date <- dates[[i]]
      res[[i]] <- make.transaction.request(start_date, end_date)
    }
  }
  
  if (tidy_transactions == TRUE) {
   
    transactions <- bind_cols(res)
  
    names(transactions) <- c("transaction_id", "transaction_event_code",
                              "date", "transaction_updated_date",
                              "currency_code", "value", "transaction_status",
                              "transaction_subject", "currency_code", "value",
                              "currency_code", "value2", "protection_eligibility")
  
  
     transactions <- transactions[, c("transaction_id", "date", "value")]
  
     transactions <- transactions %>% mutate(value = sapply(value, function(x) paste0("£", x)),
                                             date = sapply(date, function(x) format(as_date(x), format="%B %d %Y"))
     )
  }
  
  else {
    res
  }

}


transactions_live <- return.transactions(4, tidy_transactions = FALSE, live = TRUE)

# empty
transactions_sandbox <- return.transactions(4, tidy_transactions = FALSE, live = FALSE)

这是我在实时模式下得到的响应:

* Connection 90 seems to be dead!
* Closing connection 90
*   Trying 66.211.168.123...
* TCP_NODELAY set
* Connected to api.paypal.com (66.211.168.123) port 443 (#91)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api.paypal.com
*  start date: Jul 28 00:00:00 2020 GMT
*  expire date: Aug  2 12:00:00 2022 GMT
*  subjectAltName: host "api.paypal.com" matched cert's "api.paypal.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA
*  SSL certificate verify ok.
* Server auth using Basic with user 'my_username'
> POST /v1/oauth2/token HTTP/1.1
Host: api.paypal.com
Authorization: Basic blahblahQVZtUWtXlcasdklsdS3VTME93sadjksdjpYlRSXzE2Si11LTVBS19DYnFVX1BlWlBQREcwT25OeTVSTHdQOTVyV0F4WUasdjasdoOUM4bzluQmN6MGg5ckFzbWQ=
User-Agent: RStudio Desktop (1.2.5033); R (3.6.3 x86_64-apple-darwin15.6.0 x86_64 darwin15.6.0)
Accept-Encoding: deflate, gzip
Accept: application/json
Accept-Language: en_US
Content-Length: 29
Content-Type: application/x-www-form-urlencoded

* upload completely sent off: 29 out of 29 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 972
< Connection: keep-alive
< Date: Mon, 26 Oct 2020 13:05:54 GMT
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Paypal-Debug-Id: 431a3fb2c4f43
< X-Paypal-Token-Service: IAAS
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< 
* Connection #91 to host api.paypal.com left intact

除了“Strict-Transport-Security”标头外,这似乎与沙盒模式相同:

* Connection 91 seems to be dead!
* Closing connection 91
* Connection 92 seems to be dead!
* Closing connection 92
*   Trying 173.0.82.78...
* TCP_NODELAY set
* Connected to api.sandbox.paypal.com (173.0.82.78) port 443 (#93)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api.sandbox.paypal.com
*  start date: Jul 27 00:00:00 2020 GMT
*  expire date: Aug  1 12:00:00 2022 GMT
*  subjectAltName: host "api.sandbox.paypal.com" matched cert's "api.sandbox.paypal.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA
*  SSL certificate verify ok.
* Server auth using Basic with user 'sandbox_username'
> POST /v1/oauth2/token HTTP/1.1
Host: api.sandbox.paypal.com
Authorization: Basic blahblahT1I3MmJrSk5asjdkasjdkasjdksajkdjaskjdksajdkjsakdadkpasdUDF6cDBscjlnTWxaMXBScnl2WmR2YU91cGM=
User-Agent: RStudio Desktop (1.2.5033); R (3.6.3 x86_64-apple-darwin15.6.0 x86_64 darwin15.6.0)
Accept-Encoding: deflate, gzip
Accept: application/json
Accept-Language: en_US
Content-Length: 29
Content-Type: application/x-www-form-urlencoded

* upload completely sent off: 29 out of 29 bytes
< HTTP/1.1 200 OK
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Content-Length: 972
< Content-Type: application/json
< Date: Mon, 26 Oct 2020 15:07:46 GMT
< Paypal-Debug-Id: 89a06d70502df
< X-Paypal-Token-Service: IAAS
< 
* Connection #93 to host api.sandbox.paypal.com left intact

交易调用:

* Found bundle for host api.paypal.com: 0x7f835d4f5810 [can pipeline]
* Could pipeline, but not asked to!
* Re-using existing connection! (#3) with host api.paypal.com
* Connected to api.paypal.com (66.211.168.123) port 443 (#3)
> GET /v1/reporting/transactions?fields=all&start_date=2020-06-28T19:49:23Z&end_date=2020-07-28T19:49:23Z&fields=all HTTP/1.1
Host: api.paypal.com
User-Agent: RStudio Desktop (1.2.5033); R (3.6.3 x86_64-apple-darwin15.6.0 x86_64 darwin15.6.0)
Accept-Encoding: deflate, gzip
Accept: application/json
Authorization: Bearer blahsduas9fuyiwjwf_vX1d_p8n3keJy8ZSvyjZasdkfdkdsfdsLL8MgQf3RN0R9sEHAXystT7EXqlgasdjasdasosCsxjtBotQ

< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 442
< Connection: keep-alive
< Date: Mon, 26 Oct 2020 19:49:25 GMT
< Application_id: APP-09E52asjdasd0071E
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Caller_acct_num: YXGDSFU9DSFBU7TUE
< Paypal-Debug-Id: aadd1bcasduasd239
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< 
* Connection #3 to host api.paypal.com left intact

【问题讨论】:

    标签: r curl paypal paypal-sandbox


    【解决方案1】:

    当您请求访问令牌时,它需要返回范围https://uri.paypal.com/services/reporting/search/read。如果它尚未包含该范围,请在您将事务搜索权限添加到您的 REST 应用后等待最多 9 小时。

    如果您不想等待 9 小时,请在请求第一个访问令牌之前创建一个新的 REST 应用并选中事务搜索框,该令牌将被缓存 9 小时。

    【讨论】:

    • 谢谢。它确实返回了这个范围,但仍然没有交易信息通过。
    • 好吧,我不知道您自己的代码是否仍在缓存旧代码,但我们需要查看您的完整交易搜索请求 URL 和响应的日志以进一步提供帮助JSON,包括响应中的任何标头。您可以使用 -v 从 curl 重新运行请求
    • 已编辑原帖。
    • 那些似乎是 oauth 令牌请求调用,这在这一点上很好——但对你来说不起作用的是实际的交易搜索请求。因此,您的交易搜索请求 URL,并使用 curl -v 实时调用它(当然还有 -H 标头中的访问令牌)将很有用。
    • 好的,添加(我认为)。尽管有 Paypal 调试 ID,但看不到错误。
    猜你喜欢
    • 2015-06-10
    • 2023-01-01
    • 2013-02-07
    • 2023-03-23
    • 2014-03-01
    • 2016-06-29
    • 2016-09-09
    • 2017-01-24
    • 2012-04-14
    相关资源
    最近更新 更多